irpas技术客

Spring Security 做token形式登录 和 动态权限管理_weixin_46135502_spring token管理

网络 2846

文章目录 一、登录1.实现登录成功后处理器2.实现登录失败 处理器3.实现未登录直接访问失败 处理器4. 实现从数据库读取user ,实现UserDetailsService.loadUserByUsername5.实现jwt过滤器,重写BasicAuthenticationFilter6.实现Security 的配置类 ,重写WebSecurityConfigurerAdapter二、动态权限管理1. 实现获取当前url的权限标识,重写FilterInvocationSecurityMetadataSource2.实现自定义验证权限,重写AccessDecisionManager3.修改配置类,将重写类添加三、密码加密传输1.前端加密2.后端解密3.修改配置类


一、登录 1.实现登录成功后处理器 @Service("authenticationSuccessHandler") public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Autowired private JwtUtils jwtUtils; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response , Authentication authentication) throws IOException { logger.info("User: " + request.getParameter("username") + " Login successfully."); this.returnJson(response,authentication); } private void returnJson(HttpServletResponse response,Authentication authentication) throws IOException { String token = jwtUtils.generateToken(authentication); response.setStatus(HttpServletResponse.SC_OK); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json"); response.getWriter().println("{\"exceptionId\":\"null\",\"messageCode\":\"200\"," + "\"message\": \"登录成功\",\"Authorization\": \"" + token + "\"}"); } } 2.实现登录失败 处理器 @Service("authenticationFailHandler") public class AuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException { this.returnJson(response, exception); } private void returnJson(HttpServletResponse response, AuthenticationException exception) throws IOException { response.setStatus(HttpServletResponse.SC_OK); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json"); response.getWriter().println("{\"exceptionId\":\"null\",\"messageCode\":\"401\"," + "\"message\": \"" + "用户名或密码错误" + "\",\"serverTime\": " + System.currentTimeMillis() + "}"); } } 3.实现未登录直接访问失败 处理器 @Slf4j @Service("authenticationEntryPointImpl") class AuthenticationEntryPointImpl implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException { log.error("Spring Securtiy异常", e); response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); PrintWriter out = response.getWriter(); out.print(JSON.toJSONString(Result.fail(402,"未登录,请先登录!",null))); } } 4. 实现从数据库读取user ,实现UserDetailsService.loadUserByUsername @Component public class DatabaseUserDetailsService implements UserDetailsService { @Resource private TbUserMapper TbUserMapper; @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { QueryWrapper<TbUser> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username",userName); TbUser user = TbUserMapper.selectOne(queryWrapper); if (user == null) { //throw exception inform front end not this user throw new UsernameNotFoundException("user + " + userName + "not found."); } List<String> roleIdList = TbUserMapper.queryUserOwnedRoleIds(userName); List<GrantedAuthority> authorities = roleIdList.stream().map(e -> new SimpleGrantedAuthority(e)).collect(Collectors.toList()); UserDetails userDetails = new org.springframework.security.core.userdetails.User( user.getUsername(),user.getPassword(),authorities); return userDetails; } public static void main(String[] args) { BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); String encode = bCryptPasswordEncoder.encode("1234"); System.out.println(encode); } } 5.实现jwt过滤器,重写BasicAuthenticationFilter @Slf4j public class JWTAuthenticationFilter extends BasicAuthenticationFilter { public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { super(authenticationManager); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { JwtUtils jwtUtils = SpringContextUtil.getBean(JwtUtils.class); String jwt = request.getHeader("Authorization"); if (StrUtil.isBlankOrUndefined(jwt)) { chain.doFilter(request, response); return; } Claims claim = jwtUtils.getClaimByToken(jwt); if (claim == null) { throw new JwtException("token异常!"); } Date expiration = claim.getExpiration(); String username = claim.getSubject(); List<String> roleIdList = (List<String>) claim.get("roles"); if (jwtUtils.isTokenExpired(expiration)) { throw new JwtException("token已过期"); } List<GrantedAuthority> authorities = roleIdList.stream().map(e -> new SimpleGrantedAuthority(e)).collect(Collectors.toList()); UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, null , authorities); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); chain.doFilter(request, response); } } 6.实现Security 的配置类 ,重写WebSecurityConfigurerAdapter @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private DatabaseUserDetailsService userDetailsService; @Autowired @Qualifier("authenticationSuccessHandler") private AuthenticationSuccessHandler successHandler; @Autowired @Qualifier("authenticationFailHandler") private AuthenticationFailHandler failHandler; @Autowired @Qualifier("authenticationEntryPointImpl") private AuthenticationEntryPoint entryPoint; @Autowired private PasswordEncoder passwordEncoder; @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); // 使用 BCrypt 加密 } @Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setUserDetailsService(userDetailsService); authenticationProvider.setPasswordEncoder(passwordEncoder); // 设置密码加密方式 return authenticationProvider; } @Override public void configure(HttpSecurity http) throws Exception { http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and().csrf().disable().cors().and() .authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).authenticated() //允许所有人访问knife4j .antMatchers("/doc.html", "/webjars/**", "/", "/img.icons/**", "/swagger-resources/**", "/login", "/v2/api-docs").permitAll() .anyRequest().authenticated() .and().formLogin().loginProcessingUrl("/login") .successHandler(successHandler) .failureHandler(failHandler) .and().logout().logoutSuccessUrl("/login") .and().exceptionHandling().authenticationEntryPoint(entryPoint); http.addFilterBefore(new JWTAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); auth.authenticationProvider(authenticationProvider()); } @Bean public AppFilterInvocationSecurityMetadataSource mySecurityMetadataSource(FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource) { AppFilterInvocationSecurityMetadataSource securityMetadataSource = new AppFilterInvocationSecurityMetadataSource(filterInvocationSecurityMetadataSource); return securityMetadataSource; } }
二、动态权限管理 1. 实现获取当前url的权限标识,重写FilterInvocationSecurityMetadataSource public class AppFilterInvocationSecurityMetadataSource implements org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource { @Autowired private TbRoleService tbRoleService; private FilterInvocationSecurityMetadataSource superMetadataSource; private final AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } public AppFilterInvocationSecurityMetadataSource(FilterInvocationSecurityMetadataSource expressionBasedFilterInvocationSecurityMetadataSource){ this.superMetadataSource = expressionBasedFilterInvocationSecurityMetadataSource; } @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { FilterInvocation fi = (FilterInvocation) object; String url = fi.getRequestUrl(); // TODO 从数据库加载权限配置 List<PathRoleName> pathRoleNameList=tbRoleService.findPathAndRoleName(); for (PathRoleName pathRoleName : pathRoleNameList) { if (antPathMatcher.match(pathRoleName.getPath(),url)){ return SecurityConfig.createList(pathRoleName.getRoleName()); } } // 返回代码定义的默认配置 return SecurityConfig.createList("ROLE_LOGIN"); } @Override public boolean supports(Class<?> clazz) { return FilterInvocation.class.isAssignableFrom(clazz); } } 2.实现自定义验证权限,重写AccessDecisionManager @Component public class UrlAccessDecisionManager implements AccessDecisionManager { @Override public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException { Iterator<ConfigAttribute> iterator = collection.iterator(); while (iterator.hasNext()) { ConfigAttribute ca = iterator.next(); //当前请求需要的权限 String needRole = ca.getAttribute(); if ("ROLE_LOGIN".equals(needRole)) { if (authentication instanceof AnonymousAuthenticationToken) { throw new BadCredentialsException("未登录"); } else { return; } } //当前用户所具有的权限 Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); for (GrantedAuthority authority : authorities) { if (authority.getAuthority().equals(needRole)) { return; } } } throw new AccessDeniedException("权限不足!"); } @Override public boolean supports(ConfigAttribute configAttribute) { return true; } @Override public boolean supports(Class<?> aClass) { return true; } } 3.修改配置类,将重写类添加 @Override public void configure(HttpSecurity http) throws Exception { http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and().csrf().disable().cors().and() .authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).authenticated() //允许所有人访问knife4j .antMatchers("/doc.html", "/webjars/**", "/", "/img.icons/**", "/swagger-resources/**", "/login", "/v2/api-docs").permitAll() .anyRequest().authenticated() .and().formLogin().loginProcessingUrl("/login") .successHandler(successHandler) .failureHandler(failHandler) .and().logout().logoutSuccessUrl("/login") .and().exceptionHandling().authenticationEntryPoint(entryPoint) .and().authorizeRequests()// 自定义FilterInvocationSecurityMetadataSource .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { @Override public <O extends FilterSecurityInterceptor> O postProcess( O fsi) { fsi.setSecurityMetadataSource(mySecurityMetadataSource(fsi.getSecurityMetadataSource())); fsi.setAccessDecisionManager(urlAccessDecisionManager); return fsi; } }); http.addFilterBefore(new JWTAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class); }
三、密码加密传输 1.前端加密 $.ajax({ type: "post", url: "/login", data: { "username": username, "password": encrypt(password) }, success: function(res) { } }); 2.后端解密 import org.springframework.http.HttpMethod; import org.springframework.lang.Nullable; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class SignInPasswordDecryptionFilter extends OncePerRequestFilter { private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login", HttpMethod.POST.name()); @Override protected void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { if (DEFAULT_ANT_PATH_REQUEST_MATCHER.matcher(request).isMatch()) { filterChain.doFilter(new FormContentRequestWrapper(request), response); } else { filterChain.doFilter(request, response); } } private static class FormContentRequestWrapper extends HttpServletRequestWrapper { public FormContentRequestWrapper(HttpServletRequest request) { super(request); } @Override @Nullable public String getParameter(String name) { String queryStringValue = super.getParameter(name); if (UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY.equals(name)) { // 解密操作 queryStringValue = "123456"; } return queryStringValue; } } } 3.修改配置类 http.addFilterBefore(new SignInPasswordDecryptionFilter(), UsernamePasswordAuthenticationFilter.class);


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #Spring #token管理 #class #extends