springboot整合security5自定义处理认证、权限管理

首先引入security jar 包

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<!---security与thymeleaf整合jar-->
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity5</artifactId>
			<version>3.0.4.RELEASE</version>
		</dependency>

先来个配置类

/**
 * created by jasonwang
 * on 2019/7/23 14:25
 * security安全认证配置
 */
// 不要忘记
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CustomSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomUserDetailsService userDetailsService;
    @Autowired
    private CustomLoginFailureHandler loginFailureHandler;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()          // 定义当需要用户登录时候,转到的登录页面。
                .loginPage("/login")      // 设置登录页面
                .loginProcessingUrl("/pass/user/login") // 自定义的登录接口
                .successForwardUrl("/auth/index") //该请求必须是post请求
                .failureHandler(loginFailureHandler)
                .and()
                .authorizeRequests()    // 定义哪些URL需要被保护、哪些不需要被保护
                .antMatchers("/login","/pass/**","/webjars/**","/css/**", "/js/**", "/fonts/**", "/images/**")
                .permitAll()   // 不需要保护的
                .anyRequest()        // 任何请求,登录后可以访问
                .authenticated()
                .and()
                .csrf().disable();     // 关闭csrf防护 (防护跨域攻击,app服务时一般需要关闭)
        // 开启注销功能
        http.logout()
            .logoutSuccessUrl("/login")
            ;
    }

    /**
     * 密码加密规则,这是默认的,也可以自己定义
     * @return
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    //指定自定义的user服务类以及密码加密策略
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/static/**",
                "/favicon.ico"
                );
    }

}

自定义用户信息服务类
@Slf4j 若是红色,一个是要引入lombokjar包,二要在idea中配置,可参照我的另一篇文章:
https://blog.csdn.net/wjs040/article/details/96301593

@Slf4j
@Component
public class CustomUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("用户的用户名: {}", username);
        //在此处可写自己的逻辑,可与数据库关联,查找该用户的信息,若用户不存在则抛出异常
        // TODO 根据用户名,查找到对应的密码,与权限
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123456");
        //String password = "123456";
        log.info("用户的密码: {}", password);
        // 封装用户信息,并返回。参数分别是:用户名,密码,用户权限
        User user = new User("wjs040", password,
                AuthorityUtils.commaSeparatedStringToAuthorityList("SEARCHUSER,ADDUSER,UPDATEUSER"));
        return user;
    }
}

自定义验证用户名密码的逻辑

//若使用的话 去掉注释,然后在配置类中的方法中加入:
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
        //auth.authenticationProvider(new CustomAuthenticationProvider());
    }
    
//@Component  
public class CustomAuthenticationProvider extends DaoAuthenticationProvider {

    protected void additionalAuthenticationChecks(UserDetails userDetails,
                                                  UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            logger.debug("Authentication failed: no credentials provided");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }

        String presentedPassword = authentication.getCredentials().toString();
        CustomAuthenticationProvider customAuthenticationProvider = new CustomAuthenticationProvider();
        PasswordEncoder passwordEncoder = customAuthenticationProvider.getPasswordEncoder();

        if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            logger.debug("Authentication failed: password does not match stored value");

            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
    }
    //这个方法一定也要重写哦,就是把里面的注释点即可。不然启动都会报错,
    protected void doAfterPropertiesSet() throws Exception {
        //Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
    }
}

自定义处理失败管理

@Component
public class CustomLoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
    public CustomLoginFailureHandler(){
        this.setDefaultFailureUrl("/login?error=true");
    }
	//在这里面可以处理详细的失败信息,和上面的一般不要同时都使用
   /* public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response, AuthenticationException exception)
            throws IOException, ServletException {
        CustomLoginFailureHandler customLoginFailureHandler = new CustomLoginFailureHandler();
        String defaultFailureUrl = "/login";
        if(exception instanceof UsernameNotFoundException){
            defaultFailureUrl += "?error="+ ExceptionCode.USERNAMEERROR;
        } else if(exception instanceof BadCredentialsException){
            defaultFailureUrl += "?error="+ ExceptionCode.PWDERROR;
        } else{
            defaultFailureUrl += "?error="+ ExceptionCode.SYSTEMERROR;
        }

        saveException(request, exception);
        logger.debug("Redirecting to " + defaultFailureUrl);
        customLoginFailureHandler.getRedirectStrategy().sendRedirect(request, response, defaultFailureUrl);
    }*/
}

自定义请求访问验证类,即授权控制


/**
 * created by jasonwang
 * on 2019/7/24 10:09
 * 注册自定义权限验证规则,本类不需要在CustomSecurityConfig里面被@Bean,已经帮我们自动做了,
 * 如果再加入的话,就会报错。下面这段看看就行了,不用加入使用。
 * @Bean
 public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler(){
 DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler();
 handler.setPermissionEvaluator(customPermissionEvaluator);
 return handler;
 }*/
@Component
public class CustomPermissionEvaluator extends DenyAllPermissionEvaluator {
    @Autowired
    private SysPermissionService permissionService;
    @Override
    public boolean hasPermission(Authentication authentication, Object targetUrl, Object permission) {
        // 获得loadUserByUsername()方法的结果
        User user = (User)authentication.getPrincipal();
        // 获得loadUserByUsername()中注入的角色
        Collection<GrantedAuthority> authorities = user.getAuthorities();

        // 遍历用户所有角色
        for(GrantedAuthority authority : authorities) {
            String roleName = authority.getAuthority();
            //简易版的验证,GrantedAuthority这里面存放的是权限
            if(roleName.equals(permission)) return true;

            /*Integer roleId = roleService.selectByName(roleName).getId();
            // 得到角色所有的权限
            List<SysPermission> permissionList = permissionService.listByRoleId(roleId);*/
            //此处先写出固定的,用于搭建框架测试 begin
            /*List<SysPermission> permissionList = new ArrayList<>();
            SysPermission sysPermission2 = new SysPermission();
            sysPermission2.setPermission("SEARCHUSER,ADDUSER,UPDATEUSER");
            permissionList.add(sysPermission2);
            // end
            // 遍历permissionList
            for(SysPermission sysPermission : permissionList) {
                // 获取权限集
                List permissions = sysPermission.getPermissions();
                // 如果访问的Url和权限用户符合的话,返回true
                if(targetUrl.equals(sysPermission.getUrl())
                        && permissions.contains(permission)) {
                    return true;
                }
            }*/

        }

        return false;
    }
}
//controller参考例子
@GetMapping("/auth/search/user")
    @PreAuthorize("hasPermission('/user','SEARCHUSER')")
    public String searchUser(ModelMap modelMap) throws CustomException {
        SysUser sysUser = new SysUser();
        sysUser.setUserCode("1001");
        APPContext.setLoginInfo(sysUser);
        log.info("/auth/search/user");
        modelMap.put("user","/auth/search/user");
        return "index";
    }
/**
 * 权限实体类
 * created by jasonwang
 * on 2019/7/24 10:14
 * @Data、@NoArgsConstructor、@AllArgsConstructor这些注解,都是lombok帮助处理set/get和全参无参构造方法的
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysPermission implements Serializable {
    static final long serialVersionUID = 1L;

    private Integer id;

    private String url;

    private Integer roleId;

    private String permission;

    private List permissions;

    public List getPermissions() {
        return Arrays.asList(this.permission.trim().split(","));
    }

    public void setPermissions(List permissions) {
        this.permissions = permissions;
    }

}

下面附带上controller以及页面

@GetMapping("/login")
    public String login() throws CustomException {
        log.info("进入登录页");
        return "login";
    }

    /**
     * security成功后执行的url,必须是post的请求
     * @return
     */
    @PostMapping("/auth/index")
    public String index(){
        System.out.println("进入首页:index");
        return "index";
    }
    /**
     * 权限控制例子
     * @param modelMap
     * @return
     * @throws CustomException
     */
    @GetMapping("/auth/search/user")
    @PreAuthorize("hasPermission('/user','SEARCHUSER')")
    public String searchUser(ModelMap modelMap) throws CustomException {
        SysUser sysUser = new SysUser();
        sysUser.setUserCode("1001");
        APPContext.setLoginInfo(sysUser);
        log.info("/auth/search/user");
        modelMap.put("user","/auth/search/user");
        return "index";
    }
    /**
     * 不是跳转页面,必须加@ResponseBody
     * @return
     * @throws CustomException
     */
    @GetMapping("/getMytest")
    @SystemControllerLog(descrption = "查询用户信息",actionType = "4")
    @ResponseBody
    public ResultBean<Mytest> getMytestInfo() throws CustomException {
        return ResultBean.success( mytestService.selectAll());

    }

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <link rel="stylesheet" href="/webjars/bootstrap/3.4.1/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/3.4.1/css/bootstrap.min.css}">
    <script src="/webjars/jquery/1.12.4/jquery.min.js" th:src="@{/webjars/jquery/1.12.4/jquery.min.js}"></script>
    <script src="/webjars/bootstrap/3.4.1/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/3.4.1/js/bootstrap.min.js}"></script>
</head>
<body>
<form action="/pass/user/login" method="post">
    <h2>自定义登录页面</h2>
    <div class="form-group">
        <label for="username">用户名:</label>
        <input type="text" name="username" class="form-control" id="username" placeholder="username">
    </div>
    <div class="form-group">
        <label for="password">密码:</label>
        <input type="password" name="password" class="form-control" id="password" placeholder="password">
    </div>
    <span style="color: red;" th:text="用户名或密码错误" th:if="${param.error}"></span>
    <button type="submit" class="btn btn-default">登录</button>
</form>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/webjars/bootstrap/3.4.1/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/3.4.1/css/bootstrap.min.css}">
    <script src="/webjars/jquery/1.12.4/jquery.min.js" th:src="@{/webjars/jquery/1.12.4/jquery.min.js}"></script>
    <script src="/webjars/bootstrap/3.4.1/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/3.4.1/js/bootstrap.min.js}"></script>
</head>
<body>
index
<h1>登录才能查看 [[${user}]]</h1>

<br>
<br>
<!-- 判断是否登录 登录显示 div 的内容,未登录不显示 -->
<div sec:authorize="isAuthenticated()">判断是否登录</div><hr>
<!-- 输出账号名称 -->
<span sec:authentication="name"></span><hr>
<!-- 输出拥有的角色 -->
<span sec:authentication="principal.authorities"></span><hr>
<!-- 判断是否有该角色,有就显示,没有不显示 -->
<div sec:authorize="hasRole('SEARCHUSER')">判断是否有该角色("SEARCHUSER")</div><hr>
<div sec:authorize="hasRole('ROLE_USER')">判断是否有该角色("ROLE_USER")</div><hr>
<button onclick="test()">测试</button>
<script>
    function test() {
        $.get('/getMytest',function (data) {
            alert(JSON.stringify(data));
        });
    }

</script>
</body>
</html>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值