Springboot+Spring-Security+JWT Sso单点登录

2021-03-04 04:29

阅读:601

标签:parameter   nbsp   ack   验证   就是   isa   自定义   form   security+   

 单点登录中目前比较流行的一种使用方式,就是springsecurity+jwt实现无状态下用户登录;下面是对于Spring-Security进行单点登录使用token来进行交互的一种方式。第一次写博客请多多指教如果有更好的方式或者是错误的点麻烦请指教。

Spring-Security的主要几个实现类

  WebSecurityConfigurerAdapter这个类主要是用来对Spring Security的请求,拦截等进行一些配置

  ExcpeitonMappingAuthenticationFailureHandler 这个类主要是对登录失败进行处理

  SaveRequestAwareAuthenticationSucessHandler 这个类主要是对登录成功进行处理

  UserDetailsService 这个类主要是用于对权限的校验还有登录的校验

  BasicAuthenticationFilter 自定义拦截蓝主要是用于对token进行校验然后决定是否放行

从WebSecurityConfigurerAdapter进行请求,登录等请求进行配置

  

@Component
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    LoginAuthenticationSuecssHandler loginAuthenticationSuecssHandler;

    @Autowired
    LoginAuthenticationFailHandler loginAuthenticationFailHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .loginPage("/authentication/require")
                .loginProcessingUrl("/authentication/form")
                .successHandler(loginAuthenticationSuecssHandler)
                .failureHandler(loginAuthenticationFailHandler)
                .and()
                .authorizeRequests()//这个单词的意思是下面都是授权配置
                .antMatchers("/authentication/require","/authentication/form").permitAll()//把授权信息放开
                .anyRequest()       //任何请求都被授权
                .authenticated()//都需要身份认证
                .and()
                .addFilter(new AuthenticationTokenFilter(authenticationManager()))
                .authorizeRequests()
                .antMatchers(
                        "/",
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()
                .and()
                .csrf().disable();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/login.html");
    }



}

设置自定义登录跳转

@RestController
public class LoginController {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private RequestCache requestCache = new HttpSessionRequestCache();
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @RequestMapping("/authentication/require")
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED)
    public ResponseVo requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
        logger.info("requireAuthentication:当前用户未登录:访问路径{}"+request.getServletPath());
        SavedRequest savedRequest = requestCache.getRequest(request, response);
        if (savedRequest != null){
            String redirectUrl = savedRequest.getRedirectUrl();
            if (StringUtils.endsWithIgnoreCase(redirectUrl, ".html")) {
                logger.info("requireAuthentication:当前用户是PcWeb端请求:{}正在重定向");
                response.sendRedirect("http://localhost:9050/login.html");
            }
        }
        logger.info("requireAuthentication:访问的服务需要身份认证,请引导用户到登录页");
        return ResponseMeaage.error(SysConstant.LOGIN_FALL);
    }

}

 

对登录的账号密码进行对应的校验

@Component
public class MyUserDetailsService implements UserDetailsService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    @Qualifier("dbUserMapper")
    DbUserMapper dbUserMapper;

    @Autowired
    @Qualifier("httpServletRequest")
    HttpServletRequest httpServletRequest;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (!StringUtils.isEmpty(username)){
        //自定义拦截器进行处理的时候会有bean注入不了的情况
if (dbUserMapper == null && httpServletRequest == null){ dbUserMapper = SpringUtils.getBean(DbUserMapper.class); } DbUser dbUser = dbUserMapper.selectOne(new QueryWrapper().eq("username", username)); if (dbUser != null){ List list = new ArrayList(); GrantedAuthority au = new SimpleGrantedAuthority("ROLE_USER"); GrantedAuthority at = new SimpleGrantedAuthority("ROOT"); String password = dbUser.getPassword(); logger.info("loadUserByUsername:当前数据库密码:{}"+ password); return new User(username,password,list); }else{ throw new UsernameNotFoundException("用户不存在"); } }else{ throw new UsernameNotFoundException("用户名为空"); } } }

 

对登录成功的类进行返回对应jwt加密过的token

public  class LoginAuthenticationSuecssHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Autowired
    private ObjectMapper objectMapper;

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
            String header = request.getHeader("user-agent");
            logger.info("LoginAuthenticationSuecssHandler:当前登录的用户:{}"+authentication.getName());
            String password = request.getParameter("password");
            String token = JWTTest.getJwtBuilder(password, authentication.getName(), JWTTest.getKey());
            logger.info("LoginAuthenticationSuecssHandler:加密后的token:{}"+token);
            if (header.indexOf("Android") != -1) {
                response.getWriter().write(objectMapper.writeValueAsString(ResponseMeaage.sucess(token,1, SysConstant.LOGIN_SUCCESS)));
            }else if (header.indexOf("iPhone") != -1 || header.indexOf("iPad") != -1){
                response.getWriter().write(objectMapper.writeValueAsString(ResponseMeaage.sucess(token,1, SysConstant.LOGIN_SUCCESS)));
            }else{
    response.getWriter().write(objectMapper.writeValueAsString(ResponseMeaage.sucess(token,1, SysConstant.LOGIN_SUCCESS)));
            }
    }

}

登录失败的进行对应的处理

@Component
public class LoginAuthenticationFailHandler extends ExceptionMappingAuthenticationFailureHandler {
    private Logger logger = LoggerFactory.getLogger(getClass());
    @Autowired
    ObjectMapper objectMapper;
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        String header = request.getHeader("user-agent");
        if (header.indexOf("Android") != -1) {
            logger.info("onAuthenticationFailure:Phone Model:Android");
        }else if (header.indexOf("iPhone") != -1 || header.indexOf("iPad") != -1){
            logger.info("onAuthenticationFailure:Phone Model:iPhone");
        }else{
            logger.info("onAuthenticationFailure:Web{}"+header);
            response.sendRedirect("http://localhost:9030/index.html");
        }
        logger.info("LoginAuthenticationFailHandler:当前用户登录失败{}"+request.getParameter("username"));
        ResponseVo responseVo = ResponseMeaage.error("用户输入密码错误");
        response.getWriter().write(objectMapper.writeValueAsString(responseVo));
    }
}

自定义拦截器进行token的校验

@Component

public class AuthenticationTokenFilter extends BasicAuthenticationFilter {
    private Logger logger = LoggerFactory.getLogger(getClass());
    public AuthenticationTokenFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("BasicAuthenticationFilters");
        String tokenHeader = request.getParameter("token");
        System.out.println("tokenHeader:"+tokenHeader);
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "*");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "Access-Control-Allow-Headers:x-requested-with,content-type");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=utf-8");
        // 如果请求头中没有Authorization信息则直接放行了
        if (StringUtils.isEmpty(tokenHeader)) {
            chain.doFilter(request, response);
            return;
        }
        try {
            SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader));
            logger.info("LoginAuthenticationSuecssHandler:设置Token成功");
            super.doFilterInternal(request, response, chain);
        }catch (Exception e){
            logger.info("当前token验证信息出错:token{}",tokenHeader);
        }

        chain.doFilter(request, response);
        // 如果请求头中有token,则进行解析,并且设置认证信息

    }
    private UsernamePasswordAuthenticationToken getAuthentication(String token) {
        //解析Token时将“Bearer ”前缀去掉
        Claims jwtToken = JWTTest.cc(token, JWTTest.getKey());
        String username = jwtToken.get("username").toString();
        logger.info("getAuthentication:username{}",username);
        if (!StringUtils.isEmpty(username)){
            return new UsernamePasswordAuthenticationToken(username, null, null);
        }
        return null;
    }

}

自定义拦截器Bean获取不到使用该方法进行获取

@Component
public class SpringUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        if (SpringUtils.applicationContext == null) {
            SpringUtils.applicationContext = applicationContext;
        }

    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //根据name
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    //根据类型
    public static  T getBean(Class clazz) {
        return getApplicationContext().getBean(clazz);
    }

    public static  T getBean(String name, Class clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

}

JWT进行简单的加密

public class JWTTest {
    public static Key getKey(){
        Key key = new SecretKeySpec("javastack".getBytes(),
                SignatureAlgorithm.HS512.getJcaName());
        return key;
    }
    public static String getJwtBuilder(String password,String user,Key key){
        Map userMap = new HashMap();
        userMap.put("username",user);
        String jwtBuilder = Jwts.builder()
                .setClaims(userMap)
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setSubject(password)

                .signWith(SignatureAlgorithm.HS256, key.toString()).compact();
        return jwtBuilder;
    }

    public static String getJwtBuilder(String user,Key key){
        Map userMap = new HashMap();
        userMap.put("username",user);
        String jwtBuilder = Jwts.builder()
                .setClaims(userMap)
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setSubject(userMap.get("username").toString())
                .signWith(SignatureAlgorithm.HS256, key.toString()).compact();
        return jwtBuilder;
    }
    public static Claims cc(String jwtBuilder,Key key){
        Jws claimsJws = Jwts.parser().setSigningKey(key.toString()).parseClaimsJws(jwtBuilder);
        JwsHeader header = claimsJws.getHeader();
        Claims claims = claimsJws.getBody();
        System.out.println("jwt header:" + header);
        System.out.println("jwt body:" + claims);
        System.out.println("jwt body user-id:" + claims.get("password", String.class));
        return claims;

    }

}

  

Springboot+Spring-Security+JWT Sso单点登录

标签:parameter   nbsp   ack   验证   就是   isa   自定义   form   security+   

原文地址:https://www.cnblogs.com/xiaoguifu/p/14367977.html


评论


亲,登录后才可以留言!