JAVA——springSecurity——自定义配置的一些补充:Anonymous匿名用户重写loadUserByUsername()方法自定义WebSecurityConfig配置等

Posted 叶不修233

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA——springSecurity——自定义配置的一些补充:Anonymous匿名用户重写loadUserByUsername()方法自定义WebSecurityConfig配置等相关的知识,希望对你有一定的参考价值。

JAVA——springSecurity——自定义配置的一些补充:Anonymous匿名用户、重写loadUserByUsername()方法、自定义WebSecurityConfig配置等

三、一些细节补充

(1)Anonymous匿名用户

未登录的情况下发起一个非认证请求,系统会自动生成一个匿名对象anonymoususer,但这个匿名用户不包含任何权限

AnonymousAuthenticationFilter类——doFilter()方法

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException 
    //获取当前 spring security的上下文对象,从上下文对象中获取一个对象authentication(Token - 标志,象征)。
    if (SecurityContextHolder.getContext().getAuthentication() == null) 
        //如果为空,说明用户未登录(用户如果登录会往上下文中存入这个东西),那就调用createAuthentication()方法创建一个匿名用户,并把这个匿名用户存入上下文
SecurityContextHolder.getContext().setAuthentication(this.createAuthentication((HttpServletRequest)req));
            if (this.logger.isDebugEnabled()) 
                this.logger.debug("Populated SecurityContextHolder with anonymous token: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
            
         else if (this.logger.isDebugEnabled()) 
            this.logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'");
        

        chain.doFilter(req, res);

AnonymousAuthenticationFilter类——createAuthentication()方法

protected Authentication createAuthentication(HttpServletRequest request) 
    //创建一个匿名对象auth
    AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(this.key, this.principal, this.authorities);
    auth.setDetails(this.authenticationDetailsSource.buildDetails(request));
    return auth;

(2)重写loadUserByUsername()方法

要想从自定义的数据库中读取账号和密码,可以在我们自定义的配置类WebSecurityConfig中配置,只需要两步:
1.重新定制DaoAuthenticationProvider
2.指定认证管理器为自定义的管理器
3.在用户业务实现类UserServiceImpl中重写loadUserByUsername(username)方法,使用户名通过访问数据库得到
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserServiceImpl userServiceImpl;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        //在内存中配置用户名和密码
//        auth.inMemoryAuthentication().withUser("tom")
//                .password(passwordEncoder.encode("123")).roles();
//        auth.inMemoryAuthentication().withUser("admin")
//                .password(passwordEncoder.encode("admin")).roles();
        //2.指定认证管理器
        auth.authenticationProvider(getDaoAuthenticationProvider());
    


    /**
     * 1.重新定制DaoAuthenticationProvider
     * @return
     */
    @Bean
    public DaoAuthenticationProvider getDaoAuthenticationProvider()
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        //设置用户未找到的异常不隐藏
        provider.setHideUserNotFoundExceptions(false);
        //设置认证管理器使用userServiceImpl对象
        provider.setUserDetailsService(userServiceImpl);
        //设置认证管理器使用的密码管理对象
        provider.setPasswordEncoder(passwordEncoder);
        return provider;
    

/**
 * <p>
 *  服务实现类
 * </p>
 * 实现了UserDetailsService接口,重写它的loadUserByUsername方法
 * 使springSecurity从内存中读取账号密码改成从我们自定义的数据库中读取帐号密码
 * @author z
 * @since 2022-07-05
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService, UserDetailsService 
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 重写了UserDetailsService接口的loadUserByUsername方法
     * 从数据库读取用户名和密码
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException
        //3.访问数据库,根据用户名查询用户对象
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        User user = userMapper.selectOne(wrapper);
        if(user==null)
            throw new UsernameNotFoundException("用户名不存在!");
        
        //查询用户的权限集
        List<String> permList = userMapper.getPerCodesByPerm(user.getUsername());

        //封装用户的权限集
        List<GrantedAuthority> authorities = new ArrayList<>();
        permList.forEach(perm->
            authorities.add(new SimpleGrantedAuthority(perm));
        );
        Boolean isEnabled = true;
        Boolean isAccountNonExpired = true;
        Boolean isCredentialsNonExpired = true;
        Boolean isAccountNonLocked = true;

        if(user.getStatus().equals("1"))
            //用户不可用
            isEnabled = false;
        else if(user.getStatus().equals("2"))
            //账户已过期
            isAccountNonExpired=false;
        else if(user.getStatus().equals("3"))
            //凭据已过期
            isCredentialsNonExpired=false;
        else if(user.getStatus().equals("4"))
            //账户已锁定
            isAccountNonLocked=false;
        

        UserDetails userDetails =
                new org.springframework.security.core.userdetails.User(
                        user.getUsername(),
                        passwordEncoder.encode(user.getPassword()),
                        isEnabled,
                        isAccountNonExpired,
                        isCredentialsNonExpired,
                        isAccountNonLocked,
                        authorities);

        return userDetails;
    

(3)自定义响应结果ResponseResult工具类

@Data
public class ResponseResult<T> 
    private int status;
    private String msg;
    private T data;

    public ResponseResult()

    public ResponseResult(int status, String msg)
        this.status = status;
        this.msg = msg;
    
    public ResponseResult(T data, String msg, int status)
        this(status,msg);
        this.data = data;
        this.msg = msg;
    

    public static ResponseResult ok()
        ResponseResult result = new ResponseResult();
        result.setStatus(ResultCode.SUCCESS.getCode());
        result.setMsg(ResultCode.SUCCESS.getMessage());
        return result;
    

    public static ResponseResult error(ResultCode resultCode)
        ResponseResult result = new ResponseResult();
        result.setStatus(resultCode.getCode());
        result.setMsg(resultCode.getMessage());
        return result;
    

    public static ResponseResult<Void> SUCCESS = new ResponseResult<>(200,"成功");
    public static ResponseResult<Void> INTEVER_ERROR = new ResponseResult<>(500,"服务器错误");
    public static ResponseResult<Void> NOT_FOUND = new ResponseResult<>(404,"未找到");


(4)自定义状态码和对应msg工具类ResultCode

public enum ResultCode 

    /* 成功 */
    SUCCESS(200, "成功"),

    /* 默认失败 */
    COMMON_FAIL(999, "失败"),

    /* 参数错误:1000~1999 */
    PARAM_NOT_VALID(1001, "参数无效"),
    PARAM_IS_BLANK(1002, "参数为空"),
    PARAM_TYPE_ERROR(1003, "参数类型错误"),
    PARAM_NOT_COMPLETE(1004, "参数缺失"),

    /* 用户错误 */
    USER_NOT_LOGIN(2001, "用户未登录"),
    USER_ACCOUNT_EXPIRED(2002, "账号已过期"),
    USER_CREDENTIALS_ERROR(2003, "密码错误"),
    USER_CREDENTIALS_EXPIRED(2004, "密码过期"),
    USER_ACCOUNT_DISABLE(2005, "账号不可用"),
    USER_ACCOUNT_LOCKED(2006, "账号被锁定"),
    USER_ACCOUNT_NOT_EXIST(2007, "账号不存在"),
    USER_ACCOUNT_ALREADY_EXIST(2008, "账号已存在"),
    USER_ACCOUNT_USE_BY_OTHERS(2009, "您的登录已经超时或者已经在另一台机器登录,您被迫下线"),
    TOKEN_IS_NULL(2010,"TOKEN为空"),
    TOKEN_INVALID_EXCEPTION(2011,"TOKEN非法"),

    /* 业务错误 */
    NO_PERMISSION(4001, "没有权限"),

    /*部门错误*/
    DEPARTMENT_NOT_EXIST(5007, "部门不存在"),
    DEPARTMENT_ALREADY_EXIST(5008, "部门已存在"),

    /*运行时异常*/
    ARITHMETIC_EXCEPTION(9001,"算数异常"),
    NULL_POINTER_EXCEPTION(9002,"空指针异常"),
    ARRAY_INDEX_OUTOfBOUNDS_EXCEPTION(9003,"数组越界");


    ResultCode(Integer code, String message)
        this.code = code;
        this.message = message;
    

    private Integer code;
    public Integer getCode() 
        return code;
    

    private String message;
    public String getMessage() 
        return message;
    


(5)自定义WebSecurityConfig配置

主要有三项:
1.SpringSecurity 自带HttpBasic基础认证模式
2.默认formLogin表单模式
3.自定义formLogin表单模式

5-1 HttpBasic模式登录认证

SpringSecurity 自带一种基础认证模式

实现方式:创建WebSecurityConfig配置类

/**
 * Spring Securtiy配置类
 */
@Configuration  //配置类
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        super.configure(auth);
    

    @Override
    public void configure(WebSecurity web) throws Exception 
        super.configure(web);
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        //开启httpBasci模式登录认证
        http.httpBasic()
                //每个模块配置使用and结尾
                .and()
                //配置路径拦截,表明路径访问所对应的权限,角色,认证信息
                .authorizeRequests()
                .anyRequest()
                //所有请求都需要登录认证才能访问
                .authenticated();
    

5-2 默认formLogin表单模式

注释掉上面的配置类,通过applicaton.yml配置用户名与密码

spring:
    security:
            user:
                name: tom
                password: tom

5-3 自定义formLogin表单模式

继承WebSecurityConfigurerAdapter类,实现它的三个configure方法

//@Configuration
@EnableWebSecurity //涵盖了 @Configuration 注解
//@EnableGlobalMethodSecurity(jsr250Enabled = true)
//@EnableGlobalMethodSecurity(securedEnabled = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启Security注解鉴权的功能
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private UserServiceImpl userServiceImpl;
    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    @Autowired
    private MyAuthenticationEntryPoint myAuthenticationEntryPoint;
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;
    @Autowired
    private JwtTokenAuthenticationFilter jwtTokenAuthenticationFilter;
    @Autowired
    private MyLogoutSuccessHandler myLogoutSuccessHandler;
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        //在内存中配置用户名和密码
//        auth.inMemoryAuthentication().withUser("tom")
//                .password(passwordEncoder.encode("123")).roles();
//        auth.inMemoryAuthentication().withUser("admin")
//                .password(passwordEncoder.encode("admin")).roles();
        //从指定的数据库读取账号密码
//        auth.userDetailsService(userServiceImpl).passwordEncoder(passwordEncoder);
        //指定认证管理器
        auth.authenticationProvider(getDaoAuthenticationProvider());
    

    @Override
    public void configure(WebSecurity web) throws Exception 
        super.configure(web);
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        //开启httpBasic认证
//        http.httpBasic()
//                //每个模块配置使用and结尾
//                .and()
//                //配置路径拦截,表明路径访问所对应的权限,角色,认证信息
//                .authorizeRequests()
//                .anyRequest()
//                //所有请求都需要登录认证才能访问
//                .authenticated();
        //http.httpBasic().and().authorizeRequests().anyRequest().authenticated(); //关闭httpBasic认证

        //开启自定义formLogin表单认证
        //需要放行的url在这里配置,必须要放行/login和/login.html,不然会报错
        http.authorizeRequests().antMatchers("/login", "/login.html")
                .permitAll().anyRequest().authenticated().and().
                // 设置登陆页、登录表单form中action的地址,也就是处理认证请求的路径
                        formLogin().loginPage("/login.html").loginProcessingUrl("/login")
                //登录表单form中密码输入框input的name名,不修改的话默认是password
                .usernameParameter("username").passwordParameter("password")
                //登录认证成功后默认转跳的路径
//                .defaultSuccessUrl("/home")
                //登陆成功后不跳转,返回json数据
                .successHandler(myAuthenticationSuccessHandler)
                //登录认证失败后,被放行的请求
                //注意,此请求不能为error,因为已经被springSecurity定义
//                .failureUrl("/gotoError1").permitAll();
                //登录认证失败后,不跳转,返回json数据
                .failureHandler(myAuthenticationFailureHandler)
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(myAuthenticationEntryPoint)
                .accessDeniedHandler(myAccessDeniedHandler)
                        .and().logout().logoutSuccessHandler(myLogoutSuccessHandler);
        //将自定义的JwtTokenAuthenticationFilter插入到过滤器链的指定过滤器前面
        http.addFilterBefore(jwtTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        //关闭CSRF跨域
        http.csrf().disable();
        //关闭session最严格的策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    

    如何使用 Java 配置表示 Spring Security“自定义过滤器”?

SpringSecurity 自定义表单登录

具有 Spring Security 和 Java Config 的自定义身份验证提供程序

Spring security - 自定义 AuthenticationProvider 不起作用 - Java Config

springSecurity之java配置篇

springSecurity之java配置篇