1认证
认证:辨别用户是否本系统用户。
优势:1 提供多样式的加密方法
2 提供多样式的用户存储方式
3 使用者无需关注验证封装业务 只需要提供查询方法即可
4 多样式的认证方式
5 提供用户信息获取方式
可扩展的功能
1 记住我
2 邮箱验证
3 手机验证
4 验证码验证
配置详解
spring security统一实现WebSecurityConfigurerAdapter接口 按照以下需求添加以下配置 就可以整合成功
@Configuration @EnableWebSecurity // 开启springsecurity过滤链 filter @EnableGlobalMethodSecurity(prePostEnabled = true) // 开启注解方法级别权限控制 public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { //实现输入是明文 存储到数据库为密文 写死即可 @Bean public PasswordEncoder passwordEncoder() { // 明文+随机盐值》加密存储 return new BCryptPasswordEncoder(); } //用户验证的业务流程 也就是查询用户的业务代码 @Autowired UserDetailsService customUserDetailsService; /** * 认证管理器: 将上文查询用户是否存在的service按样式写入 修改service 其余写死 还有密码写死或存放内存中等方式 这里不讨论 * 1. 认证信息(用户名,密码) * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 数据库存储的密码必须是加密后的也就是必须要有passwordEncoder,不然会报错:There is no PasswordEncoder mapped for auth.userDetailsService(customUserDetailsService); } //验证码配置 @Autowired private ImageCodeValidateFilter imageCodeValidateFilter; //当验证成功后可以返回json或者路径 但是现在基于前后台分离 大多数都是返回json AuthenticationSuccessHandler为成功后转为json的处理 按照本文配置即可 @Autowired private AuthenticationSuccessHandler customAuthenticationSuccessHandler; //当验证成功后可以返回json或者路径 但是现在基于前后台分离 大多数都是返回json AuthenticationFailureHandler为失败后转为json的处理 按照本文配置即可 @Autowired private AuthenticationFailureHandler customAuthenticationFailureHandler; //建立数据源 @Autowired DataSource dataSource; @Autowired private InvalidSessionStrategy invalidSessionStrategy; /** * 当同个用户session数量超过指定值之后 ,会调用这个实现类 */ @Autowired private SessionInformationExpiredStrategy sessionInformationExpiredStrategy; /** * 持久化token * @return */ @Bean public JdbcTokenRepositoryImpl jdbcTokenRepository() { JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); jdbcTokenRepository.setDataSource(dataSource); // 是否启动项目时自动创建表,true自动创建 // jdbcTokenRepository.setCreateTableOnStartup(true); return jdbcTokenRepository; } /** * 核心拦截器 当你认证成功之后 ,springsecurity它会重写向到你上一次请求上 * 资源权限配置: * 1. 被拦截的资源 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { //调用验证码过滤器 下文会详细介绍 http.addFilterBefore(imageCodeValidateFilter, UsernamePasswordAuthenticationFilter.class) .formLogin() // 表单登录方式 .loginPage("/login/page") //登录页的页面地址 .loginProcessingUrl("/login/form") // 登录表单提交处理url, 默认是/login .usernameParameter("username") //默认的是 username .passwordParameter("password") // 默认的是 password .successHandler(customAuthenticationSuccessHandler) //登录成功返回的json .failureHandler(customAuthenticationFailureHandler) //登录失败返回的json .and() //每个类型的配置 以.and()间隔 相当于; .authorizeRequests() // 授权请求 .antMatchers("/login/page", "/code/image","/mobile/page", "/code/mobile", "/code/image", "/code/mobile", "/mobile/page" ).permitAll() // 放行/login/page不需要认证可访问 因为如果在调用验证接口时还需要权限 那么就没有入口了 所以一些不需要登录就能访问的接口在此配置 // 此处是鉴权 // 有 sys:user 权限的可以访问任意请求方式的/role .antMatchers("/user").hasAuthority("sys:user") // 有 sys:role 权限的可以访问 get方式的/role .antMatchers(HttpMethod.GET,"/role").hasAuthority("sys:role") .antMatchers(HttpMethod.GET, "/permission") // ADMIN 注意角色会在前面加上前缀 ROLE_ , 也就是完整的是 ROLE_ADMIN, ROLE_ROOT .access("hasAuthority('sys:premission') or hasAnyRole('ADMIN', 'ROOT')") // 此处是鉴权 .anyRequest().authenticated() //所有访问该应用的http请求都要通过身份认证才可以访问 .and() .rememberMe() //记住我功能 //记住功能配置 .tokenRepository(jdbcTokenRepository()) //保存token信息 .tokenValiditySeconds(604800) //记住我有效时长 .and() .sessionManagement()// session管理 .invalidSessionStrategy(invalidSessionStrategy) //当session失效后的处理类 //.expiredSessionStrategy(sessionInformationExpiredStrategy)// 当用户达到最大session数后,则调用此处的实现 .maximumSessions(1) // 每个用户在系统中最多可以有多少个session .maxSessionsPreventsLogin(true) // 当一个用户达到最大session数,则不允许后面再登录 .sessionRegistry(sessionRegistry()) .and().and() .logout()//登出相关 .addLogoutHandler(customLogoutHandler) // 退出清除缓存 .logoutUrl("/user/logout") // 退出请求路径 .logoutSuccessUrl("/mobile/page") //退出成功后跳转地址 .deleteCookies("JSESSIONID") // 退出后删除什么cookie值 ;// 注意不要少了分号 http.csrf().disable(); // 关闭跨站请求伪造 } /** * 退出清除缓存 */ @Autowired private CustomLogoutHandler customLogoutHandler; /** * 为了解决退出重新登录问题 * @return */ @Bean public SessionRegistry sessionRegistry() { return new SessionRegistryImpl(); } /** * 一般是针对静态资源放行 * @param web * @throws Exception */ @Override public void configure(WebSecurity web){ web.ignoring().antMatchers("/js/**", "/css/**"); } }
清除缓存方法
@Component public class CustomLogoutHandler implements LogoutHandler { @Autowired private SessionRegistry sessionRegistry; @Override public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { // 退出之后 ,将对应session从缓存中清除 SessionRegistryImpl.principals sessionRegistry.removeSessionInformation(request.getSession().getId()); } }
编写user验证方法类
需要继承UserDetailsService 为jar包提供
1. public interface UserDetailsService { 2. UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException; 3. }
获取SysUser实体方法和获取此人菜单的方法 这里不多说
public abstract class AbstractUserDetailsService implements UserDetailsService { @Autowired private SysPermissionService sysPermissionService; /** * 这个方法交给子类去实现它,查询用户信息 * * @param usernameOrMobile 用户名或者手机号 * @return */ public abstract SysUser findSysUser(String usernameOrMobile); @Override public UserDetails loadUserByUsername(String usernameOrMobile) throws UsernameNotFoundException { // 1. 通过请求的用户名去数据库中查询用户信息 SysUser sysUser = findSysUser(usernameOrMobile); // 通过用户id去获取权限信息 findSysPermission(sysUser); return sysUser; } private void findSysPermission(SysUser sysUser) { if (sysUser == null) { throw new UsernameNotFoundException("用户名或密码错误"); } // 2. 查询该用户有哪一些权限 List<SysPermission> permissions = sysPermissionService.findByUserId(sysUser.getId()); if (CollectionUtils.isEmpty(permissions)) { return; } // 在左侧菜单 动态渲染会使用,目前先把它都传入 sysUser.setPermissions(permissions); // 3. 封装权限信息 List<GrantedAuthority> authorities = Lists.newArrayList(); for (SysPermission sp : permissions) { // 权限标识 String code = sp.getCode(); authorities.add(new SimpleGrantedAuthority(code)); } sysUser.setAuthorities(authorities); } }
@Component("customUserDetailsService") public class CustomUserDetailsService extends AbstractUserDetailsService{ Logger logger = LoggerFactory.getLogger(getClass()); @Autowired // 不能删掉,不然报错 PasswordEncoder passwordEncoder; @Autowired SysUserService sysUserService; @Override public SysUser findSysUser(String usernameOrMobile) { logger.info("请求认证的用户名: " + usernameOrMobile); // 1. 通过请求的用户名去数据库中查询用户信息 return sysUserService.findByUsername(usernameOrMobile); } }
CustomAuthenticationSuccessHandler与CustomAuthenticationFailureHandler
//此类没有注入,因为楼主并没有使用 @Component("customAuthenticationSuccessHandler") public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Autowired(required = false) // 容器中可以不需要有接口的实现,如果有则自动注入 AuthenticationSuccessListener authenticationSuccessListener; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { if(authenticationSuccessListener != null) { // 当认证之后 ,调用此监听,进行后续处理,比如加载用户权限菜单 authenticationSuccessListener.successListener(request, response, authentication); } if(LoginResponseType.JSON.equals( "post")) { // 认证成功后,响应JSON字符串 MengxueguResult result = MengxueguResult.ok("认证成功"); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(result.toJsonString()); }else { //重定向到上次请求的地址上,引发跳转到认证页面的地址 logger.info("authentication: " + JSON.toJSONString(authentication)); super.onAuthenticationSuccess(request, response, authentication); } } }
@Component("customAuthenticationFailureHandler") public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { /** * * @param exception 认证失败时抛出异常 */ @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { if(LoginResponseType.JSON.equals("post")) { // 认证失败响应JSON字符串, MengxueguResult result = MengxueguResult.build(HttpStatus.UNAUTHORIZED.value(), exception.getMessage()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(result.toJsonString()); }else { // 重写向回认证页面,注意加上 ?error // super.setDefaultFailureUrl(securityProperties.getAuthentication().getLoginPage()+"?error"); // 获取上一次请求路径 String referer = request.getHeader("Referer"); logger.info("referer:" + referer); // 如果下面有值,则认为是多端登录,直接返回一个登录地址 Object toAuthentication = request.getAttribute("toAuthentication"); String lastUrl = toAuthentication != null ? "/login/page" : StringUtils.substringBefore(referer,"?"); logger.info("上一次请求的路径 :" + lastUrl); super.setDefaultFailureUrl(lastUrl+"?error"); super.onAuthenticationFailure(request, response, exception); } } }