springboot本身已经提供了很好的spring security的支持,我们只需要实现(或者重写)一部分接口来实现我们的个性化设置即可。此处浅显易懂,没有深入原理(后面文章替换,有需要的小伙伴稍等等~~~)。
思路:
1.通过弹簧安全做授权拦截操作
2.通过JWT根据用户信息生成令牌以供后面调用
3.将生成的令牌放到HttpServletResponse的头信息中
4.使用的时候从response头中获取令牌放在request头中提交到后台做认证即可
5. 交替超时时间10天

一,绒球

常规还是先上pom,因为pom可以很直观的看到本项目用了些东西,我这个项目使用了很多的包,这里贴出了核心的几个,其他大部分的都会自动引用。

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-security</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.security</groupId>
  7. <artifactId>spring-security-web</artifactId>
  8. <scope>provided</scope>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.security</groupId>
  12. <artifactId>spring-security-config</artifactId>
  13. <scope>provided</scope>
  14. </dependency>
  15. <dependency>
  16. <groupId>io.jsonwebtoken</groupId>
  17. <artifactId>jjwt</artifactId>
  18. </dependency>

二,登录过滤器

我们采用倒推法,用到什么找什么,这也比较符合XP编程的思想,不写多(无)余(用)的代码,既然要做认证,很明显需要一个过滤器来处理所有需要拦截1.
用户名PasswordAuthenticationFilter是安全性自己提供的过滤器,我们重写其中的成功方法(successfulAuthentication)来处理我们自己的逻辑,当然根据自己的情况,某些登录失败处理,重写(unsuccessfulAuthentication)即可
。1。成功地中用到一个令牌认证处理程序,即令牌认证处理类,该类的主要方法就是借用jwt的机制来生成令牌,以供后面登录授权使用
。2.往响应头信息中放入参数为“授权”,估值“ Bearer” + token的值

  1. package com.mos.eboot.tools.jwt;
  2. import com.mos.eboot.tools.util.FastJsonUtils;
  3. import org.springframework.security.authentication.AuthenticationManager;
  4. import org.springframework.security.core.Authentication;
  5. import org.springframework.security.core.userdetails.UserDetails;
  6. import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
  7. import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
  8. import javax.servlet.FilterChain;
  9. import javax.servlet.ServletException;
  10. import javax.servlet.http.HttpServletRequest;
  11. import javax.servlet.http.HttpServletResponse;
  12. import java.io.IOException;
  13. public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter{
  14. static final String TOKEN_PREFIX = "Bearer";
  15. static final String HEADER_STRING = "Authorization";
  16. private AuthenticationSuccessHandler successHandler;
  17. public JWTLoginFilter() {
  18. }
  19. public JWTLoginFilter(AuthenticationManager authManager) {
  20. setAuthenticationManager(authManager);
  21. }
  22. @Override
  23. protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException, ServletException {
  24. TokenAuthenticationHandler tokenAuthenticationHandler = new TokenAuthenticationHandler();
  25. Object obj = auth.getPrincipal();
  26. if(obj != null) {
  27. UserDetails userDetails = (UserDetails)obj;
  28. String token = tokenAuthenticationHandler.generateToken(FastJsonUtils.toJSONNoConfig(userDetails));
  29. res.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + token);
  30. }
  31. if(successHandler != null) {
  32. successHandler.onAuthenticationSuccess(req, res, auth);
  33. }
  34. }
  35. public void setSuccessHandler(AuthenticationSuccessHandler successHandler) {
  36. this.successHandler = successHandler;
  37. }
  38. }

JWTAuthenticationToken

  1. package com.mos.eboot.tools.jwt;
  2. import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  3. import static java.util.Collections.emptyList;
  4. public class JWTAuthenticationToken extends UsernamePasswordAuthenticationToken{
  5. private static final long serialVersionUID = 1L;
  6. public JWTAuthenticationToken(Object principal) {
  7. super(principal,null,emptyList());
  8. }
  9. @Override
  10. public Object getCredentials() {
  11. return super.getCredentials();
  12. }
  13. @Override
  14. public Object getPrincipal() {
  15. return super.getPrincipal();
  16. }
  17. }

令牌认证处理程序

  1. package com.mos.eboot.tools.jwt;
  2. import io.jsonwebtoken.Claims;
  3. import io.jsonwebtoken.Jwts;
  4. import io.jsonwebtoken.SignatureAlgorithm;
  5. import java.io.Serializable;
  6. import java.util.Date;
  7. import java.util.HashMap;
  8. import java.util.Map;
  9. /**
  10. * @author 小尘哥
  11. */
  12. public class TokenAuthenticationHandler implements Serializable {
  13. private static final long serialVersionUID = 1L;
  14. private static final String CLAIM_KEY_CREATED = "created";
  15. private static final String CLAIM_KEY_SUBJECT = "subject";
  16. private static final String DEFAULT_SECRET = "eboot@secret";
  17. private static final Long DEFAULT_EXPIRATION = 864000L;
  18. private String secret = DEFAULT_SECRET;
  19. private Long EXPIRATION = DEFAULT_EXPIRATION;
  20. public TokenAuthenticationHandler() {
  21. }
  22. public String getSubjectFromToken(String token) {
  23. String subject;
  24. try {
  25. final Claims claims = getClaimsFromToken(token);
  26. subject = claims.get(CLAIM_KEY_SUBJECT).toString();
  27. } catch (Exception e) {
  28. subject = null;
  29. }
  30. return subject;
  31. }
  32. private Claims getClaimsFromToken(String token) {
  33. Claims claims;
  34. try {
  35. claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
  36. } catch (Exception e) {
  37. claims = null;
  38. }
  39. return claims;
  40. }
  41. private Date generateExpirationDate() {
  42. return new Date(System.currentTimeMillis() + EXPIRATION * 1000);
  43. }
  44. public String generateToken(String subject) {
  45. Map<String, Object> claims = new HashMap<String, Object>();
  46. claims.put(CLAIM_KEY_CREATED, new Date());
  47. claims.put(CLAIM_KEY_SUBJECT, subject);
  48. return generateToken(claims);
  49. }
  50. String generateToken( Map<String, Object> claims) {
  51. return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate())
  52. .signWith(SignatureAlgorithm.HS512, secret).compact();
  53. }
  54. }

三,认证

从头信息中取回授权,然后解析出个人信息,如果个人信息不为空,则将个人信息加密后再加入授权域。

  1. package com.mos.eboot.tools.jwt;
  2. import org.apache.commons.lang3.StringUtils;
  3. import org.springframework.security.core.context.SecurityContextHolder;
  4. import org.springframework.web.filter.GenericFilterBean;
  5. import javax.servlet.FilterChain;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. import javax.servlet.http.HttpServletRequest;
  10. import java.io.IOException;
  11. public class JWTAuthenticationFilter extends GenericFilterBean {
  12. static final String HEADER_STRING = "Authorization";
  13. static final String TOKEN_PREFIX = "Bearer";
  14. @Override
  15. public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
  16. HttpServletRequest req = (HttpServletRequest)request;
  17. String token = req.getHeader(HEADER_STRING);
  18. if(StringUtils.isNotBlank(token) && token.startsWith(TOKEN_PREFIX)) {
  19. TokenAuthenticationHandler tokenAuthenticationHandler = new TokenAuthenticationHandler();
  20. String subject = tokenAuthenticationHandler.getSubjectFromToken(token.replace(TOKEN_PREFIX, ""));
  21. if(StringUtils.isNotBlank(subject)) {
  22. SecurityContextHolder.getContext().setAuthentication(new JWTAuthenticationToken(subject));
  23. }
  24. }
  25. filterChain.doFilter(request,response);
  26. }
  27. }

四,调用

  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception {
  3. http.csrf().disable().authorizeRequests().antMatchers("/**").authenticated()
  4. .antMatchers(HttpMethod.POST, "/login").permitAll().anyRequest().permitAll().and()
  5. .addFilterBefore(loginFilter(), UsernamePasswordAuthenticationFilter.class)
  6. .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
  7. }
  8. //注入登录校验类
  9. @Bean
  10. public JWTLoginFilter loginFilter() throws Exception {
  11. JWTLoginFilter loginFilter = new JWTLoginFilter(authenticationManager());
  12. loginFilter.setSuccessHandler(loginAuthenticationSuccessHandler);
  13. loginFilter.setAuthenticationFailureHandler((request, response, exception) -> {
  14. response.setContentType("application/json");
  15. response.getWriter().write(FastJsonUtils
  16. .toJSONString(new ResultModel(ResultStatus.FAIL.getCode(), exception.getMessage())));
  17. });
  18. return loginFilter;
  19. }
  20. @Bean
  21. public DaoAuthenticationProvider authenticationProvider() {
  22. DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
  23. authenticationProvider.setUserDetailsService(userDetailsService());
  24. authenticationProvider.setPasswordEncoder(passwordEncoder());
  25. authenticationProvider.setHideUserNotFoundExceptions(false);
  26. return authenticationProvider;
  27. }
  28. @Bean
  29. @Override
  30. public UserDetailsService userDetailsService() {
  31. return new UserService();
  32. }
  33. //重写密码加密方法
  34. @Bean
  35. public Md5PasswordEncoder passwordEncoder() {
  36. Md5PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
  37. passwordEncoder.setIterations(1);
  38. return passwordEncoder;
  39. }

实现UserDetailsService接口,定义自己的获取用户登录方法实现类

  1. package com.mos.eboot.api.config.support;
  2. import com.mos.eboot.api.platform.api.ISysUserService;
  3. import com.mos.eboot.platform.entity.SysUser;
  4. import com.mos.eboot.tools.result.ResultModel;
  5. import org.springframework.security.core.userdetails.UserDetails;
  6. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  7. import org.springframework.stereotype.Service;
  8. import javax.annotation.Resource;
  9. /**
  10. * @author 小尘哥
  11. */
  12. @Service("userService")
  13. public class UserService implements IUserService {
  14. @Resource
  15. private ISysUserService sysUserService;
  16. @Override
  17. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  18. return sysUserService.getByUsername(username);
  19. }
  20. }