1.登录接口调用
authentication = authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(username, password));
2.
extends WebSecurityConfigurerAdapter
重写
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); }
3.
implements UserDetailsService
重写
loadUserByUsername(String username)
4.
自定义 LoginUser
implements UserDetails
5.
JwtAuthenticationTokenFilter extends OncePerRequestFilter
========================================= @Component public class SysLoginService { @Autowired private TokenService tokenService; @Resource private AuthenticationManager authenticationManager; @Autowired private RedisCache redisCache; /** * 登录验证 * * @param username 用户名 * @param password 密码 * @param code 验证码 * @param uuid 唯一标识 * @return 结果 */ public String login(String username, String password, String code, String uuid) { String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; String captcha = redisCache.getCacheObject(verifyKey); /*获取缓存中的数据uuid +code 并删除code删除验证码*/ redisCache.deleteObject(verifyKey); /*判断验证码是否正确*/ if (captcha == null) { AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); throw new CaptchaExpireException(); } if (!code.equalsIgnoreCase(captcha)) { AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); throw new CaptchaException(); } // 用户验证 Authentication authentication = null; try { /** * !!!看我看我看我!!! * 看我少走弯路,获取用户对象的时候,会去调用下面的这个方法查询用户对象 * */ // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername System.out.println("username "+username+" -----password "+password); /*????怎么实现的认证管理器 自定义一个全局认证的配置类SecurityConfig(WebSecurityConfigurerAdapter) */ /*返回ture 或者false (第一步) 调用抽象类WebSecurityConfigurerAdapter 我们去实现这个类*/ authentication = authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(username, password)); }catch (Exception e) { e.printStackTrace(); if (e instanceof BadCredentialsException) { /*异步日志*/ AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); throw new UserPasswordNotMatchException(); }else { AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); throw new CustomException(e.getMessage()); } } AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); LoginUser loginUser = (LoginUser) authentication.getPrincipal(); // 生成token return tokenService.createToken(loginUser);/*String类型的Token*/ } }
=====================================
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { /** * 自定义用户认证逻辑 */ @Autowired private UserDetailsService userDetailsService; /** * 认证失败处理类 */ @Autowired private AuthenticationEntryPointImpl unauthorizedHandler; /** * 退出处理类 */ @Autowired private LogoutSuccessHandlerImpl logoutSuccessHandler; /** * token认证过滤器 */ @Autowired private JwtAuthenticationTokenFilter authenticationTokenFilter; /** * 跨域过滤器 */ @Autowired private CorsFilter corsFilter; /** * 解决 无法直接注入 AuthenticationManager * * @return * @throws Exception */ @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /** * anyRequest | 匹配所有请求路径 * access | SpringEl表达式结果为true时可以访问 * anonymous | 匿名可以访问 * denyAll | 用户不能访问 * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 * hasRole | 如果有参数,参数表示角色,则其角色可以访问 * permitAll | 用户可以任意访问 * rememberMe | 允许通过remember-me登录的用户访问 * authenticated | 用户登录后可访问 */ @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity // CSRF禁用,因为不使用session .csrf().disable() // 认证失败处理类 .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() // 基于token,所以不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // 过滤请求 .authorizeRequests() // 对于登录login 验证码captchaImage 允许匿名访问 .antMatchers("/login", "/captchaImage","/swagger-ui.html").anonymous() .antMatchers( HttpMethod.GET, "/*.ttf", "/*.woff", "/*.gif", "/*.eot", "/*.json", "/*.woff2", "/*.png", "/*.ico", "/*.svg", "/*.jpg", "/*.html", "/**/*.ttf", "/**/*.woff", "/**/*.gif", "/**/*.eot", "/**/*.json", "/**/*.woff2", "/**/*.png", "/**/*.ico", "/**/*.svg", "/**/*.jpg", "/**/*.html", "/**/*.css", "/**/*.js", "/**/swagger-ui.html", "/swagger-resources/**" ,"/webjars/**" ,"/v2/**" ,"/swagger-ui.html/**" ).permitAll(). antMatchers( //mybatis复习相关的接口全部放行,同学们可以通过postMan进行测试而不需要进行权限认证 "/review/**", "/review" ).permitAll() .antMatchers("/common/downloadByMinio**").permitAll() .antMatchers("/profile/**").anonymous() .antMatchers("/common/download**").anonymous() .antMatchers("/common/download/resource**").anonymous() .antMatchers("/webjars/**").anonymous() .antMatchers("/*/api-docs").anonymous() .antMatchers("/druid/**").anonymous() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() .and() .headers().frameOptions().disable(); httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); // 添加JWT filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // 添加CORS filter httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); } /** * 强散列哈希加密实现 */ @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } /** * (第二步)身份认证接口 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); } }
===========================================
/** *(第三步) 用户验证处理 */ @Service public class UserDetailsServiceImpl implements UserDetailsService { private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); @Autowired private ISysUserService userService; @Autowired private SysPermissionService permissionService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //从数据库查 SysUser user = userService.selectUserByUserName(username); if (StringUtils.isNull(user)) { log.info("登录用户:{} 不存在.", username); throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { log.info("登录用户:{} 已被删除.", username); throw new BaseException("对不起,您的账号:" + username + " 已被删除"); } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) { log.info("登录用户:{} 已被停用.", username); throw new BaseException("对不起,您的账号:" + username + " 已停用"); } /*返回一个拥有权限的对象*/ return createLoginUser(user); } public UserDetails createLoginUser(SysUser user) { /*通过用户名查询用户权限信息*/ return new LoginUser(user, permissionService.getMenuPermission(user)); } }
================================
/** *(第四步) 用户权限处理自己写接口mapper实现 * * */ @Component public class SysPermissionService { @Autowired private ISysRoleService roleService; @Autowired private ISysMenuService menuService; /** * 获取角色数据权限 * * @param user 用户信息 * @return 角色权限信息 */ public Set<String> getRolePermission(SysUser user) { Set<String> roles = new HashSet<String>(); // 管理员拥有所有权限 if (user.isAdmin()) { roles.add("admin"); } else { roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); } return roles; } /** * 获取菜单数据权限 * * @param user 用户信息 * @return 菜单权限信息 */ public Set<String> getMenuPermission(SysUser user) { Set<String> perms = new HashSet<String>(); // 管理员拥有所有权限 if (user.isAdmin()) { perms.add("*:*:*"); } else { perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); } return perms; } }
======================
/** * token过滤器 验证token有效性 * * */ @Component public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Autowired private TokenService tokenService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { LoginUser loginUser = tokenService.getLoginUser(request); if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) { tokenService.verifyToken(loginUser); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } chain.doFilter(request, response); } }
======================================
/** * 登录用户身份权限 * * */ public class LoginUser implements UserDetails { private static final long serialVersionUID = 1L; /** * 用户唯一标识 */ private String token; /** * 登录时间 */ private Long loginTime; /** * 过期时间 */ private Long expireTime; /** * 登录IP地址 */ private String ipaddr; /** * 登录地点 */ private String loginLocation; /** * 浏览器类型 */ private String browser; /** * 操作系统 */ private String os; /** * 权限列表 */ private Set<String> permissions; /** * 用户信息 */ private SysUser user; public String getToken() { return token; } public void setToken(String token) { this.token = token; } public LoginUser() { } public LoginUser(SysUser user, Set<String> permissions) { this.user = user; this.permissions = permissions; } @JsonIgnore @Override public String getPassword() { return user.getPassword(); } @Override public String getUsername() { return user.getUserName(); } /** * 账户是否未过期,过期无法验证 */ @JsonIgnore @Override public boolean isAccountNonExpired() { return true; } /** * 指定用户是否解锁,锁定的用户无法进行身份验证 * * @return */ @JsonIgnore @Override public boolean isAccountNonLocked() { return true; } /** * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 * * @return */ @JsonIgnore @Override public boolean isCredentialsNonExpired() { return true; } /** * 是否可用 ,禁用的用户不能身份验证 * * @return */ @JsonIgnore @Override public boolean isEnabled() { return true; } public Long getLoginTime() { return loginTime; } public void setLoginTime(Long loginTime) { this.loginTime = loginTime; } public String getIpaddr() { return ipaddr; } public void setIpaddr(String ipaddr) { this.ipaddr = ipaddr; } public String getLoginLocation() { return loginLocation; } public void setLoginLocation(String loginLocation) { this.loginLocation = loginLocation; } public String getBrowser() { return browser; } public void setBrowser(String browser) { this.browser = browser; } public String getOs() { return os; } public void setOs(String os) { this.os = os; } public Long getExpireTime() { return expireTime; } public void setExpireTime(Long expireTime) { this.expireTime = expireTime; } public Set<String> getPermissions() { return permissions; } public void setPermissions(Set<String> permissions) { this.permissions = permissions; } public SysUser getUser() { return user; } public void setUser(SysUser user) { this.user = user; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; } }
=============================================
@PreAuthorize("hasPermi('system:config:list')")//方法上直接设置访问权限
@PreAuthorize("@ss.hasPermi('system:config:list')")
===========================
/** * 自定义权限实现,ss取自SpringSecurity首字母 * @author wgl */ @Service("ss") public class PermissionService { /** 所有权限标识 */ private static final String ALL_PERMISSION = "*:*:*"; /** 管理员角色权限标识 */ private static final String SUPER_ADMIN = "admin"; private static final String ROLE_DELIMETER = ","; private static final String PERMISSION_DELIMETER = ","; @Autowired private TokenService tokenService; /** * 验证用户是否具备某权限 * * @param permission 权限字符串 * @return 用户是否具备某权限 */ public boolean hasPermi(String permission) { if (StringUtils.isEmpty(permission)) { return false; } LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) { return false; } return hasPermissions(loginUser.getPermissions(), permission); } /** * 验证用户是否不具备某权限,与 hasPermi逻辑相反 * * @param permission 权限字符串 * @return 用户是否不具备某权限 */ public boolean lacksPermi(String permission) { return hasPermi(permission) != true; } /** * 验证用户是否具有以下任意一个权限 * * @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表 * @return 用户是否具有以下任意一个权限 */ public boolean hasAnyPermi(String permissions) { if (StringUtils.isEmpty(permissions)) { return false; } LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) { return false; } Set<String> authorities = loginUser.getPermissions(); for (String permission : permissions.split(PERMISSION_DELIMETER)) { if (permission != null && hasPermissions(authorities, permission)) { return true; } } return false; } /** * 判断用户是否拥有某个角色 * * @param role 角色字符串 * @return 用户是否具备某角色 */ public boolean hasRole(String role) { if (StringUtils.isEmpty(role)) { return false; } LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { return false; } for (SysRole sysRole : loginUser.getUser().getRoles()) { String roleKey = sysRole.getRoleKey(); if (SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) { return true; } } return false; } /** * 验证用户是否不具备某角色,与 isRole逻辑相反。 * * @param role 角色名称 * @return 用户是否不具备某角色 */ public boolean lacksRole(String role) { return hasRole(role) != true; } /** * 验证用户是否具有以下任意一个角色 * * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 * @return 用户是否具有以下任意一个角色 */ public boolean hasAnyRoles(String roles) { if (StringUtils.isEmpty(roles)) { return false; } LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest()); if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) { return false; } for (String role : roles.split(ROLE_DELIMETER)) { if (hasRole(role)) { return true; } } return false; } /** * 判断是否包含权限 * * @param permissions 权限列表 * @param permission 权限字符串 * @return 用户是否具备某权限 */ private boolean hasPermissions(Set<String> permissions, String permission) { return permissions.contains(ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); } }