4 Star 0 Fork 0

梦残 / 学习笔记

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
spring security.txt 34.66 KB
一键复制 编辑 原始数据 按行查看 历史
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219
1.pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
2.properties
#数据库配置
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.url= jdbc:mysql://192.168.31.26:3306/test?useUnicode=yes&characterEncoding=UTF-8&useSSL=false
spring.datasource.username = root
spring.datasource.password = root
#session
spring.session.store-type = none
server.session.timeout = 600
#是否开启security
#security.basic.enabled = false
3.configuration
用户名默认是user,密码在控制台
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() //这里是表单登录
//http.httpBasic() //默认登录方式
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
4.原理
过滤器链
5.自定义用户认证逻辑
1.处理用户信息获取逻辑
UserDetailsService实现类
@Component
public class MyUserDetailsService implements UserDetailsService{
@Autowired
private User user;
private Logger logger = org.slf4j.LoggerFactory.getLogger(getClass());
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("登录用户名:" + username);
//根据用户名查找用户信息,这里应该去查询数据库
/**
* 参数分别为
* 账户,密码,权限
*/
return new User(username, "123456", AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}
2.处理用户校验逻辑
UserDetails接口封装了登录的所有信息
//返回用户的权限信息,不能为空
Collection<? extends GrantedAuthority> getAuthorities();
//密码
String getPassword();
//用户名,不能为空
String getUsername();
//账户没有过期
boolean isAccountNonExpired();
//账户没有被锁(冻结用户可恢复)
boolean isAccountNonLocked();
//密码是否过去
boolean isCredentialsNonExpired();
//账户是否可用(逻辑删除,删除不能恢复)
boolean isEnabled();
3.处理密码加解密
PasswordEncoder 接口
//加密,需要自己手动调用
String encode(CharSequence rawPassword);
//是否匹配,框架自己调用
boolean matches(CharSequence rawPassword, String encodedPassword);
在1的方法里面加
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); //推荐使用这个加密解密
}
6.个性化用户认证流程
1.自定义登录页面
1.修改登录配置
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")
.and()
.authorizeRequests()
.antMatchers("/login.html").permitAll() //访问该配置不需要身份认证
.anyRequest()
.authenticated()
.and()
.csrf().disable(); //security的跨站伪造防护,暂时关闭,不然下面登录要报错
}
}
2.添加登录页面
src/main/resources下添加resources包,添加login.html
<h2>标准登录页面</h2>
<h3>表单登录</h3>
<form action="/authentication/form" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><button type="submit">登录</button></td>
</tr>
</table>
</form>
2.自定义登录处理的controller
将1.1的loginPage改成controller的路径.loginPage("/login.html")
满足不同需求进入不同的登录页面
1.在配置文件中添加
study.security.browser.loginPage = /demo-signIn.html
2.添加 demo-signIn.html 页面
3.添加BrowserProperties类,SecurityProperties类
public class BrowserProperties {
private String loginPage;
public String getLoginPage() {
return loginPage;
}
public void setLoginPage(String loginPage) {
this.loginPage = loginPage;
}
}
/**
* 读取以study.security开头的属性
* @author majie
*
*/
@ConfigurationProperties(prefix = "study.security")
public class SecurityProperties {
private BrowserProperties browser = new BrowserProperties();
public BrowserProperties getBrowser() {
return browser;
}
public void setBrowser(BrowserProperties browser) {
this.browser = browser;
}
}
4.添加
/**
* 使SecurityProperties读取配置文件生效
* @author majie
*
*/
@Configuration
@EnableConfigurationProperties(SecurityProperties.class)
public class SecurityCoreConfig {
}
5.controller
@RestController
public class BrowserSecurityController {
private Logger logger = org.slf4j.LoggerFactory.getLogger(getClass());
//请求缓存
private RequestCache requestCache = new HttpSessionRequestCache();
//重定向策略
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Autowired
private SecurityProperties securityProperties;
/**
* 当需要身份认证时候跳转到这里
* @return
* @throws IOException
*/
@RequestMapping("/authentication/require")
public String requireAuthentication(HttpServletRequest request,HttpServletResponse response) throws IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if(savedRequest != null) {
String redirectUrl = savedRequest.getRedirectUrl();
logger.info("引发跳转的请求是:" + redirectUrl);
//是html的请求直接跳走,不是就要做验证,跳到BrowserProperties配置的路径
if(StringUtils.endsWithIgnoreCase(redirectUrl, ".html")) {
redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage());
}
}
return "访问的服务需要身份认证,请引导用户到登录页"; //这里应该封装
}
}
6.修改
BrowserSecurityConfig类
注入
@Autowired
private SecurityProperties securityProperties; //它里面的路径也不需要权限认证
在原来权限的地方再加上就好
3.自定义登录成功处理
4.自定义失败处理
7.添加图形验证码
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-security</artifactId>
</dependency>
开发生成图形验证码的接口
1.根据随机数生成图片
2.将随机数存到session(redis)
3.将生成的 图片写到接口的响应中
实现:
public class ValidateCode {
private String code;
private LocalDateTime expireTime;
public ValidateCode(String code, int expireIn){
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expireIn);
}
public ValidateCode(String code, LocalDateTime expireTime){
this.code = code;
this.expireTime = expireTime;
}
public boolean isExpried() {
return LocalDateTime.now().isAfter(expireTime);
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public LocalDateTime getExpireTime() {
return expireTime;
}
public void setExpireTime(LocalDateTime expireTime) {
this.expireTime = expireTime;
}
}
/**
* 验证码
* @author majie
*
*/
public class ImageCode extends ValidateCode {
private BufferedImage image;
public ImageCode(BufferedImage image, String code, int expireIn){
super(code, expireIn);
this.image = image;
}
public ImageCode(BufferedImage image, String code, LocalDateTime expireTime){
super(code, expireTime);
this.image = image;
}
public BufferedImage getImage() {
return image;
}
public void setImage(BufferedImage image) {
this.image = image;
}
}
controller
@RestController
public class ValidateCodeController {
private static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";
//操作session
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
@GetMapping("/code/image")
public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException {
//生成图片
ImageCode imageCode = createImageCode(request);
//写入session
sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, imageCode);
//将生产的图片写入应用中
ImageIO.write(imageCode.getImage(), "JPGE", response.getOutputStream());
}
private ImageCode createImageCode(HttpServletRequest request) {
//方法略
return null;
}
}
login.html中添加
<tr>
<td>图形验证码:</td>
<td>
<input type="text" name="imageCode">
<img src="/code/image?width=200">
</td>
</tr>
如果校验输入验证码
/**
* 验证码是否有效的判断
* @author majie
*
*/
@Component("validateCodeFilter")
public class ValidateCodeFilter extends OncePerRequestFilter{
//异常时候调用该处理器
private AuthenticationFailureHandler authenticationFailureHandler;
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
//如果是登陆请求就去校验,不是登陆请求直接跳过
if("/authentication/form".equals(request.getRequestURI())
&& "post".equalsIgnoreCase(request.getMethod())) {
try {
validate(new ServletWebRequest(request));
}catch (ValidateCodeException e) {
authenticationFailureHandler.onAuthenticationFailure(request, response, e);
return;
}
}
filterChain.doFilter(request, response);
}
/**
* 校验
* @param servletWebRequest
* @throws ServletRequestBindingException
*/
private void validate(ServletWebRequest request) throws ServletRequestBindingException {
//取出session信息
ImageCode iamgeCode = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.SESSION_KEY);
String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");
if(StringUtils.isEmpty(codeInRequest)) {
throw new ValidateCodeException("验证码的值不能为空");
}
//然后判断iamgeCode
//移除session
sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
}
}
/**
* 验证异常类
*/
public class ValidateCodeException extends AuthenticationException {
private static final long serialVersionUID = -7285211528095468156L;
public ValidateCodeException(String msg) {
super(msg);
}
}
修改BrowserSecurityConfig
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private SecurityProperties securityProperties; //它里面的路径也不需要权限认证
@Autowired
protected AuthenticationSuccessHandler myAuthenticationSuccessHandler; //自己实现的接口
@Autowired
protected AuthenticationFailureHandler mycAuthenticationFailureHandler; //自己实现的接口
@Override
protected void configure(HttpSecurity http) throws Exception {
//添加自己的过滤器
ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
validateCodeFilter.setAuthenticationFailureHandler(mycAuthenticationFailureHandler);
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin()
.loginPage("/authentication/require")
.loginProcessingUrl("/authentication/form") //表单提交的路径
.successHandler(myAuthenticationSuccessHandler) //成功的处理
.failureHandler(mycAuthenticationFailureHandler) //失败的处理
.and()
.authorizeRequests()
.antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage()).permitAll() //访问该配置不需要身份认证
.anyRequest()
.authenticated()
.and()
.csrf().disable(); //security的跨站伪造防护,暂时关闭
}
}
重构图形验证码接口
验证码基本参数可配置(三级配置)
请求配置 默认值在调用接口的时候传递
|
|
|覆盖
应用级配置 配置值写在代码里
|
|
|覆盖
默认配置 配置值写在代码里
1.封装验证码配置类
/**
* 验证码配置类
* @author majie
*
*/
public class ImageCodeProperties {
private int width = 67;
private int height = 23;
private int lenght = 4;
private int expireIn = 60;
public int getWidth() {
return width;
}
....GET /SET
}
2./**
* 封装所有配偶类 验证码/短信验证码
* @author majie
*
*/
public class ValidateCodeProperties {
private ImageCodeProperties imageCodeProperties = new ImageCodeProperties();
public ImageCodeProperties getImageCodeProperties() {
return imageCodeProperties;
}
public void setImageCodeProperties(ImageCodeProperties imageCodeProperties) {
this.imageCodeProperties = imageCodeProperties;
}
}
3.在SecurityProperties中添加ValidateCodeProperties属性
private ValidateCodeProperties validateCodeProperties = new ValidateCodeProperties();
4.应用级配置.在properties下
study.security.code.image.length = 6
5.请求级配置ValidateCodeController
注入 //配置文件
@Autowired
private SecurityProperties securityProperties;
修改图片生成方法
//生成图片方法
private ImageCode generate(ServletWebRequest request) {
int width = ServletRequestUtils.getIntParameter(request.getRequest(), "width", securityProperties.getCode().getImage().getWidth());
int height = ServletRequestUtils.getIntParameter(request.getRequest(), "height", securityProperties.getCode().getImage().getHeight());
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics g = image.getGraphics();
Random random = new Random();
g.setColor(getRandColor(200,250));
g.fillRect(0, 0, width, height);
g.setFont(new Font("Time New Roman", Font.ITALIC, 20));
g.setColor(getRandColor(160,200));
for(int i = 0;i < 155; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int x1 = random.nextInt(12);
int y1 = random.nextInt(12);
g.drawLine(x, y, x + x1, y + y1);
}
String sRand = "";
for(int i = 0;i < securityProperties.getCode().getImage().getLenght(); i++) {
String rand = String.valueOf(random.nextInt(10));
sRand += rand;
g.setColor(new Color(20, random.nextInt(110),
20 + random.nextInt(110),20 + random.nextInt(110)));
g.drawString(rand, 13 * i + 6, 16);
}
g.dispose();
return new ImageCode(image, sRand, securityProperties.getCode().getImage().getExpireIn());
}
/**
* 生成随机背景条纹
*
* @param fc
* @param bc
* @return
*/
private Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > 255) {
fc = 255;
}
if (bc > 255) {
bc = 255;
}
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
6.修改登录页
/code/image?width=200
验证码拦截的接口可配置
假如多个请求需要验证图片验证码
1.ImageCodeProperties类添加url属性
2.配置文件
#这两个url都会去做验证码判断
study.security.code.image.url = /user,/user/*
3.修改ValidataCodeFilter,实现InitializingBean的目的是其他参数都校验完成以后去初始化urls的值
添加
private Set<String> urls = new HashSet<>();
@Autowired
private SecurityProperties securityProperties;
//spring的工具类
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
String[] configUrls = StringUtils.split(securityProperties.getCode().getImage().getUrl(), ",");
for (String string : configUrls) {
urls.add(string);
}
urls.add("/authentication/form"); //登录请求肯定要校验
}
doFilterInternal修改
boolean action = false;
for (String url : urls) {
//有任何一个请求匹配就校验
antPathMatcher.match(url, request.getRequestURI());
action = true;
}
4.BrowserSecurityConfig配置
validateCodeFilter.setSecurityProperties(securityProperties);
validateCodeFilter.afterPropertiesSet(); //初始化方法
验证码的生成逻辑可配置
需要把逻辑封装到接口里面
1.添加接口
/**
* 验证码接口的封装
* @author majie
*
*/
public interface ValidateCodeGenerate {
ImageCode generate(ServletWebRequest request);
}
2.接口实现类
将ValidateCodeController中的验证码生成代码移到ImageCodeGenerator(上面接口的实现类)
3.ValidateCodeController注入
@Autowired
private ValidateCodeGenerate imageCodeGenerate;
4./**
* 验证码配置类
* @author majie
*
*/
@Configuration
public class ValidateCodeBeanConfig {
@Autowired
private SecurityProperties securityProperties;
@Bean
//不存在的时候才去用下面的配置,已增量的方式适应变化
@ConditionalOnMissingBean(name = "imageCodeGenerator")
public ValidateCodeGenerate imageCodeGenerator() {
ImageCodeGenerator codeGenerator = new ImageCodeGenerator();
//这里有疑问
codeGenerator.setSecurityProperties(securityProperties);
return codeGenerator;
}
}
8.记住我功能
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
原理:
实现:
1.
<tr> <!-- name要写成remeber-me -->
<td colspan='2'><input name="remember-me" type="checkbox" value="true" />记住我</td>
</tr>
2.修改BrowserSecurityConfig
@Autowired
private DataSource dataSource; //将rember-me数据存入数据库
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
tokenRepository.setCreateTableOnStartup(true); //启动的时候自动创建表
return tokenRepository;
}
3.BrowserProperties类添加属性
private int rememberMeSeconds = 3600;
BrowserSecurityConfig添加
@Autowired
private UserDetailsService userDetailsService;
配置加
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
.userDetailsService(userDetailsService)
9.短信验证登陆
开发短信验证码接口
1.添加短信验证码类,就是之前的ValidateCode类
2.图片验证码和短息验证码多了一个图片类,所以继承ValidateCode类
3.修改ValidateCodeGenerate接口,返回ValidateCode类,并修改相关提示错误
4.短信发送接口
/**
* 短信验证码发送接口
* @author majie
*
*/
public interface SmsCodeSender {
void send(String mobile,String code);
}
5.默认实现类
/**
* 默认的短信发送实现类
* @author majie
*
*/
public class DefaultSmsCodeSender implements SmsCodeSender{
//这里应该是调用运行商发送短信
@Override
public void send(String mobile, String code) {
System.out.println("向手机"+ mobile + "发送短信验证码"+ code);
}
}
6.ValidateCodeBeanConfig配置中添加
@Bean
//不存在的时候才去用下面的配置
@ConditionalOnMissingBean(name = "smsCodeSender")
public SmsCodeSender smsCodeSender() {
return new DefaultSmsCodeSender();
}
7.添加短信验证码配置类
/**
* 短信验证码配置类
* @author majie
*
*/
public class SmsCodeProperties {
//默认6位
private int lenght = 6;
private int expireIn = 60;
private String url;
}
图片验证码继承该类。
8.添加短信验证码生成实现类
/**
* 短信验证码生成实现类
* @author majie
*
*/
@Component("smsCodeGenerator")
public class SmsCodeGenerator implements ValidateCodeGenerate{
@Autowired
private SecurityProperties securityProperties;
@Override
public ValidateCode generate(ServletWebRequest request) {
String code = RandomStringUtils.randomNumeric(securityProperties.getCode().getSms().getLenght());
return new ValidateCode(code, securityProperties.getCode().getSms().getExpireIn());
}
public SecurityProperties getSecurityProperties() {
return securityProperties;
}
public void setSecurityProperties(SecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
}
9.修改controller,添加方法
@GetMapping("/code/sms")
public void createSmsCode(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletRequestBindingException {
ValidateCode smsCode = smsCodeGenerator.generate(new ServletWebRequest(request));
//写入session
sessionStrategy.setAttribute(new ServletWebRequest(request), SESSION_KEY, smsCode);
//短信验证码发送
smsCodeSender.send(ServletRequestUtils.getRequiredStringParameter(request, "mobile"), smsCode.getCode());
}
10.页面
<h3>短信登录</h3>
<form action="/authentication/mobile" method="post">
<table>
<tr>
<td>手机号:</td>
<td><input type="text" name="mobile" value="13012345678"></td>
</tr>
<tr>
<td>短信验证码:</td>
<td>
<input type="text" name="smsCode">
<a href="/code/sms?mobile=13012345678">发送验证码</a>
</td>
</tr>
<tr>
<td colspan="2"><button type="submit">登录</button></td>
</tr>
</table>
</form>
添加:
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
11.短信验证码和图片验证码类似,使用模板方法重构,太复杂。。。。没懂
/**
* 校验码处理器,封装不同校验码的处理逻辑
*
*/
public interface ValidateCodeProcessor {
/**
* 验证码放入session时的前缀
*/
String SESSION_KEY_PREFIX = "SESSION_KEY_FOR_CODE_";
/**
* 创建校验码
*
* @param request
* @throws Exception
*/
void create(ServletWebRequest request) throws Exception;
/**
* 校验验证码
*
* @param servletWebRequest
* @throws Exception
*/
void validate(ServletWebRequest servletWebRequest);
}
校验短信验证码并登陆
短信验证码配置
spring social开发第三方登录
OAuth协议简介
微信举例
原来的问题:
应用可以访问用户在微信上的所有数据
用户只有修改密码,才能收回授权
密码泄露的可能性大大提高
OAuth协议通过Token解决了上面的问题
主要角色:
provider:服务器提供商
Authorization Server:认证服务器
Resource Server:资源服务器,用户数据
resource owner:资源所有者
client:第三方用户
步骤:
0.用户访问client
1.client请求授权
2.用户授权
3.client去认证服务器申请token
4.服务器发放token
5.clien去资源服务器申请资源
6.资源服务器发放资源
四种授权模式:
1.授权码模式(authorization code) ------ 最主要的
2.简化模式(implicit)
3.密码模式(resource owner password credentials)
4.客户端模式(client credentials)
Spring Social接口(抽象类)
ServiceProvider(AbstractOAuth2ServiceProvider)
OAuth2Operations(OAut2Template)
Api(AbstractOAuth2ApiBinding)
和第七步相关的类
UserConnectionRepository(JdbcUserConnectionRepository) ----->操作 DB UserConnection
Connection(OAuth2Connection)
ConnectionFactory(OAuth2ConnectionFactory)
包含ServiceProvider
ApiAdapter
QQ登录:
1.建立QQ用户信息类
QQUserInfo
2.建立QQ接口
/**
* QQ接口
* @author majie
*/
public interface QQ {
QQUserInfo getUserInfo();
}
3.实现类QQImpl 都要继承 AbstractOAuth2ApiBinding
AbstractOAuth2ApiBinding包含两个属性 accessToken(令牌),restTemplate(发送http请求)
public class QQImpl extends AbstractOAuth2ApiBinding implements QQ {
private static final String URL_GET_OPENID = "https://graph.qq.com/oauth2.0/me?access_token=%s";
private static final String URL_GET_USERINFO = "https://graph.qq.com/user/get_user_info?oauth_consumer_key=%s&openid=%s";
private String appId;
private String openId;
private ObjectMapper objectMapper = new ObjectMapper();
public QQImpl(String accessToken, String appId) {
super(accessToken, TokenStrategy.ACCESS_TOKEN_PARAMETER);
this.appId = appId;
String url = String.format(URL_GET_OPENID, accessToken);
String result = getRestTemplate().getForObject(url, String.class);
System.out.println(result);
this.openId = StringUtils.substringBetween(result, "\"openid\":\"", "\"}");
}
@Override
public QQUserInfo getUserInfo() {
String url = String.format(URL_GET_USERINFO, appId, openId);
String result = getRestTemplate().getForObject(url, String.class);
System.out.println(result);
QQUserInfo userInfo = null;
try {
userInfo = objectMapper.readValue(result, QQUserInfo.class);
userInfo.setOpenId(openId);
return userInfo;
} catch (Exception e) {
throw new RuntimeException("获取用户信息失败", e);
}
}
}
4.QQServiceProvider 继承 AbstractOAuth2ServiceProvider
public class QQServiceProvider extends AbstractOAuth2ServiceProvider<QQ> {
private String appId;
private static final String URL_AUTHORIZE = "https://graph.qq.com/oauth2.0/authorize";
private static final String URL_ACCESS_TOKEN = "https://graph.qq.com/oauth2.0/token";
//必须要传入QQOAuth2Template
public QQServiceProvider(String appId, String appSecret) {
super(new QQOAuth2Template(appId, appSecret, URL_AUTHORIZE, URL_ACCESS_TOKEN));
this.appId = appId;
}
@Override //图中的api
public QQ getApi(String accessToken) {
return new QQImpl(accessToken, appId);
}
}
-------------------跟服务器相关代码结束-----------------------------------
5.实现Adapter
public class QQAdapter implements ApiAdapter<QQ> {
@Override
public boolean test(QQ api) {
return true;
}
@Override
public void setConnectionValues(QQ api, ConnectionValues values) {
QQUserInfo userInfo = api.getUserInfo();
values.setDisplayName(userInfo.getNickname());
values.setImageUrl(userInfo.getFigureurl_qq_1());
values.setProfileUrl(null); //微博主页
values.setProviderUserId(userInfo.getOpenId());
}
@Override
public UserProfile fetchUserProfile(QQ api) {
// TODO Auto-generated method stub
return null;
}
@Override
public void updateStatus(QQ api, String message) {
//do noting qq没有这个
}
}
6.继承ConnectionFactory
Connection在上面的代码已经处理,这里不需要处理
public class QQConnectionFactory extends OAuth2ConnectionFactory<QQ> {
public QQConnectionFactory(String providerId, String appId, String appSecret) {
super(providerId, new QQServiceProvider(appId, appSecret), new QQAdapter());
}
}
7.通过第三方登录获取到用户的信息
MyUserDetailsService 还要在实现 SocialUserDetailsService
重写loadUserByUserId方法
8.添加qq登录配置
/**
* QQ配置
* @author majie
*
*/
public class QQProperties extends SocialProperties {
/**
* 服务提供商的id
*/
private String providerId = "qq";
public String getProviderId() {
return providerId;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
}
/**
* 社交类配置
* @author majie
*
*/
public class SocialProperties {
private QQProperties qq = new QQProperties();
}
然后再SecurityProperties配置中添加SocialProperties
9./**
* QQ的连接工厂
* @author majie
*/
@Configuration
//只有在配置才生效
@ConditionalOnProperty(prefix = "imooc.security.social.qq", name = "app-id")
public class QQAutoConfig extends SocialAutoConfigurerAdapter {
@Autowired
private SecurityProperties securityProperties;
@Override
protected ConnectionFactory<?> createConnectionFactory() {
QQProperties qqConfig = securityProperties.getSocial().getQq();
return new QQConnectionFactory(qqConfig.getProviderId(), qqConfig.getAppId(), qqConfig.getAppSecret());
}
}
10.把qq登录配置到spring security的过滤链上
在socialConfig中添加bean
@Bean
public SrpingSocialConfigurer imoocSocialSecurityConfigurer() {
rentrun new SrpingSocialConfigurer();
}
在浏览器的安全配置中引入该配置
@Autowired
private SrpingSocialConfigurer imoocSrpingSocialConfigurer;
安全配置添加
.apply(imoocSocialSecurityConfig)
11.页面
<h3>社交登录</h3>
<a href="/qqLogin/callback.do">QQ登录</a>
12.个性化配置不然只能请求/auth路径
public class ImoocSpringSocialConfigurer extends SpringSocialConfigurer {
private String filterProcessesUrl;
public ImoocSpringSocialConfigurer(String filterProcessesUrl) {
this.filterProcessesUrl = filterProcessesUrl;
}
@SuppressWarnings("unchecked")
@Override
protected <T> T postProcess(T object) {
SocialAuthenticationFilter filter = (SocialAuthenticationFilter) super.postProcess(object);
filter.setFilterProcessesUrl(filterProcessesUrl);
return (T) filter;
}
}
13.修改10
@Bean
public SpringSocialConfigurer imoocSocialSecurityConfig() {
String filterProcessesUrl = securityProperties.getSocial().getFilterProcessesUrl();
ImoocSpringSocialConfigurer configurer = new ImoocSpringSocialConfigurer(filterProcessesUrl);
configurer.signupUrl(securityProperties.getBrowser().getSignUpUrl());
return configurer;
}
14.默认不会出来html类型的请求,需要重写.回报什么singin路径的
public class QQOAuth2Template extends OAuth2Template {
private Logger logger = LoggerFactory.getLogger(getClass());
public QQOAuth2Template(String clientId, String clientSecret, String authorizeUrl, String accessTokenUrl) {
super(clientId, clientSecret, authorizeUrl, accessTokenUrl);
setUseParametersForClientAuthentication(true); //设置成true才会带上clentId,和clientSerc
}
/**
* 将字符串转成类
* 默认是将json转成类
*/
@Override
protected AccessGrant postForAccessGrant(String accessTokenUrl, MultiValueMap<String, String> parameters) {
String responseStr = getRestTemplate().postForObject(accessTokenUrl, parameters, String.class);
logger.info("获取accessToke的响应:"+responseStr);
String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(responseStr, "&");
String accessToken = StringUtils.substringAfterLast(items[0], "=");
Long expiresIn = new Long(StringUtils.substringAfterLast(items[1], "="));
String refreshToken = StringUtils.substringAfterLast(items[2], "=");
return new AccessGrant(accessToken, null, refreshToken, expiresIn);
}
/**
* 添加文本类型处理
*/
@Override
protected RestTemplate createRestTemplate() {
RestTemplate restTemplate = super.createRestTemplate();
restTemplate.getMessageConverters().add(new StringHttpMessageConverter(Charset.forName("UTF-8")));
return restTemplate;
}
}
http://localhost:8080/oauth/authorize?response_type=code&client_id=mj&redirect_uri=http://www.baidu.com&scope=all
Java
1
https://gitee.com/mengcan/study-note.git
git@gitee.com:mengcan/study-note.git
mengcan
study-note
学习笔记
master

搜索帮助