spring security 搭建

一,认证(只定义用户和密码)

1.1实现UserDetailsService

重写loadUserByUsername
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired(required = false)
    @Lazy
    private PasswordEncoder passwordEncoder;
    @Autowired(required = false)
    private UserDao userDao;
    /**
     * 根据页面传递的account,至数据库查询信息,存入security中
     * @param account   页面的username
     */
    @Override
    public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {
        Users users = userDao.queryAuth(account);
        if (!Objects.isNull(users)){
            String auths = String.join(",",users.getAuths());
            return new User(users.getAccount(),passwordEncoder.encode(users.getPassword()),
                    AuthorityUtils.commaSeparatedStringToAuthorityList(auths));
        }else {
            throw new UsernameNotFoundException("用户名或密码错误");
        }

    }
}

1.2,继承WebSecurityConfigurerAdapter

@Component
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsServiceImpl userDetailsServiceImpl;
    @Autowired
    private LoginSuccessHandler loginSuccessHandler;
    @Autowired
    private JWTFilter jwtFilter;
    @Bean
    public PasswordEncoder passwordEncoder(){
        return  new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsServiceImpl);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
//                .loginPage("/login.html")//使用自定义登录界面
//                .loginProcessingUrl("/doLogin")//表单提交地址,对应表单Action
                .successHandler(loginSuccessHandler)//登录成功
                .failureHandler(new LoginFailedHandler())//登录失败
                .permitAll();//放开 login.html和dologin 的访问权
        http.exceptionHandling()
                        .accessDeniedHandler(new MyAccessDeniedHandler())//无权限
                        .authenticationEntryPoint(new MyAuthenticationEntryPointHandler());//未登录
        http.authorizeRequests()
                .anyRequest().authenticated();//所有请求都被拦截
        http.csrf().disable();//关闭跨站脚本攻击

        //前后端项目中要禁用掉session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
		//注册过滤器
        http.addFilterAfter(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

二,认证(自定义登录页面,非前后端分离项目用)

2.1,配置4个处理类

登录成功:AuthenticationSuccessHandler
登录失败:AuthenticationFailureHandler
未登录访问资源:AuthenticationEntryPoint
没有权限访问资源:AccessDeniedHandler
登录成功代码例子:

@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Override
    @SneakyThrows
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //获取用户登录信息,获取jwt字符串
        User user = (User) authentication.getPrincipal();
        String jwt = JWTUtil.createJWT(user.getUsername());
        //将信息存入redis中
        redisTemplate.opsForValue()
                .set("jwt:"+user.getUsername(),jwt, 30,TimeUnit.MINUTES);

        response.setContentType("application/json;charset=utf-8");
        PrintWriter pw = response.getWriter();
        String success = JSON.toJSONString(new ResponseResult<>().ok(jwt));

        pw.print(success);
        pw.flush();
        pw.close();
    }
}

2.2,编写JWT 工具类

/**
 * JWT工具类
 * 1.创建jwt
 * 2.校验JWT合法性
 * 3.返回载荷部分
 */
public class JWTUtil {
    public static  String key = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
    /**
     * 加密
     */
    public static String createJWT(String usernme) throws Exception{
        //1.创建jwt头部
        JWSHeader jwsHeader = new JWSHeader
                .Builder(JWSAlgorithm.HS256)
                .type(JOSEObjectType.JWT)
                .build();

        //2.创建荷载,保存用户信息
        Map userMap = new HashMap();
        userMap.put("username",usernme);
        Payload payload = new Payload(userMap);

        //3.创建签名构造器,
        //----3.1将头部与荷载拼接
        JWSObject jwsObject = new JWSObject(jwsHeader,payload);
        //----3.2根据密钥将jwsObject加密
        JWSSigner jwsSigner = new MACSigner(key);
        jwsObject.sign(jwsSigner);

        return jwsObject.serialize();
    }

    /**
     * 解密判断
     * @param jwt
     */
    public static boolean decode(String jwt) throws Exception{
        //将jwt字符串转成一个jwt对象
        JWSObject parse = JWSObject.parse(jwt);
        //将对象通过密钥解密
        JWSVerifier jwsVerifier = new MACVerifier(key);
        return parse.verify(jwsVerifier);
    }

    /**
     * 获取荷载内容
     * @param jwt
     * @return
     */
    public static Map getPayload(String jwt) throws Exception{
        JWSObject jwsObject = JWSObject.parse(jwt);
        Map payload = jwsObject.getPayload().toJSONObject();
        return payload;
    }
}

三,security 整合 jwt

继承 OncePerRequestFilter

/**
 * 1.判断用户请求是否携带jwt凭证,   是:继续执行下面逻辑,否:放行不处理
 * 2.判断携带的jwt是否合法,        是:继续执行,否:放行不处理
 * 3.用户携带的jwt与redis的jwt进行对比:
 * 3.1根据key判断redis是否存在(key获取方式:解析jwt载荷)
 * 3.2判断两个jwt值是否相同
 */
@Component
public class JWTFilter extends OncePerRequestFilter {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private UserDetailsServiceImpl userDetailsServiceImpl;

    @SneakyThrows
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String jwt = request.getHeader("jwt");
        //1.
        if (jwt == null) {
            filterChain.doFilter(request, response);
            return;
        }

        //2.
        if (!JWTUtil.decode(jwt)) {
            filterChain.doFilter(request, response);
            return;
        }

        //3.
        Map payload = JWTUtil.getPayload(jwt);
        String username = (String) payload.get("username");

        String redisJWT = redisTemplate.opsForValue().get("jwt:" + username);
        if (redisJWT == null) {
            filterChain.doFilter(request, response);
            return;
        }
        if (!jwt.equals(redisJWT)) {
            filterChain.doFilter(request, response);
            return;
        }

        //token续期
        redisTemplate.opsForValue().set("jwt:" + username, jwt, 30, TimeUnit.MINUTES);

        UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(username);
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        filterChain.doFilter(request, response);//放行到下一个过滤器
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值