Spring Security用户授权管理

613 阅读3分钟

这里记录一下Spring Security对登录用户请求访问页面的一些基本的授权控制,页面已经准备好,前端使用Thymeleaf模板引擎

用户授权管理

自定义访问控制

在之前也提到,需要自定义访问请求的权限控制,则需要在配置类中添加相应的接口配置,即重写configue(HttpSecurity http)

/*开启安全管理配置*/
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsServiceImpl userDetailsService;

    /*自定义身份认证*/
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        /*密码编译器*/
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

        /*1.基于内存的身份认证*/
//        auth.inMemoryAuthentication().passwordEncoder(encoder)
//                .withUser("admin").password(encoder.encode("admin")).roles("admin")
//                .and()
//                .withUser("cai").password(encoder.encode("123457")).roles("common");

        /*2.使用UserDetails进行身份认证*/
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
    }


    /*自定义用户权限*/
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /*自定义访问控制*/
        http.authorizeRequests()
        	//表示放行“/”访问,可根据自己需要调整
                .antMatchers("/").permitAll()  
                //根据用户拥有的不同权限配置访问权限
                .antMatchers("/admin/**").hasAuthority("admin")
                .antMatchers("/common/**").hasAuthority("common")
                .and()
                .formLogin();
    }
}

自定义用户登录页面

在之前项目中的登录一直使用的是Spring Security自带的登录页面,如果需要使用自己的登录页面

登录页面login.html

<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1,shrink-to-fit=no">

    <title>用户登录界面</title>
    <link rel="stylesheet" th:href="@{/bootstrap.min.css}">
    <link rel="stylesheet" th:href="@{/signin.css}">
</head>

<body>

<!--SpringSecurity:登录提交数据验证的请求路径必须和跳转到登录页的请求路径一致-->
<form class="form-signin" th:action="@{/toLoginPage}"   method="post">

    <img class="mb-4"  width="72" height="72" th:src="@{/login/img/login.jpg}">
    <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">请登录</h1>
    <div  style="color: red" th:text="${logintips}"></div>
    
    <!-- ***************  -->
    <input type="text" class="form-control" required="" autofocus=""  name="username" th:placeholder="#{login.username}">
    <input type="password" class="form-control" required=""name="password" th:placeholder="#{login.password}">
    <div class="checkbox mb-3">
        <label><input type="checkbox" name="rememberme">[[#{login.rememberme}]]</label>
    </div>
    <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.button}">登录</button>
    <!-- ***************  -->
    
    <p class="mt-5 mb-3 text-muted">© <span th:text="${currentYear}">2020</span>-<span th:text="${currentYear}+1">2019</span></p>
    <a class="btn btn-sm" th:href="@{/toLoginPage(langurage='zh_CN')}">中文</a>
    <a class="btn btn-sm" th:href="@{/toLoginPage(langurage='en_US')}">English</a>
</form>

</body>
</html>

登录提交数据验证的请求路径必须和跳转到登录页的请求路径一致

跳转到登录页的Controller接口

    /*访问登录页*/
    @GetMapping("/toLoginPage")
    public String toLogingPage(Model model)S
    {
    	//设置当前年份
        model.addAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));
        return "login";
    }

路径一致

  • login.html登录页提交登录数据验证的路径
<form class="form-signin" th:action="@{/toLoginPage}"   method="post">
  • 访问登录页的请求路径
@GetMapping("/toLoginPage")

在SecurityConfig配置类内设置

SecurityConfig是之前就已经写好的Spring Security配置类,这里面需要加入几行代码,在重写的configure(HttpSecurity http)方法内部编写

  • 允许登录页login.html能够访问
  • 自定义登录页控制(登录页对应的请求页、接受用户名和密码参数名、登陆成功跳转的路径、失败跳转的路径)
    /*自定义用户权限*/
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
        /*自定义登录页*/
        http.formLogin()
        	//开放访问权限,所有请求均可访问
                .loginPage("/toLoginPage").permitAll()
                //对应页面表单的name属性
                .usernameParameter("username")
                .passwordParameter("password")
                .defaultSuccessUrl("/")
                //失败路径
                .failureUrl("/toLoginPage?error");
    }

这样,就完成了自定义登录页面的需求

自定义用户退出

注销功能的实现比较简单,只需要简短的两行代码就能实现

首先提供一个注销的表单

<form th:action="@{/mylogout}" method="post">
	<input th:type="submit" th:value="注销" />
</form>

配置securityConfig

同样是在configure(HttpSecurity http)方法内部编写

  /*自定义用户退出*/
  http.logout()
      .logoutUrl("/mylogout")   //注销对应的请求路径
      .logoutSuccessUrl("/");   //注销后跳转的路径

前端页面控制

不同的用户有不同的角色,因此有时候在一个页面中,希望普通用户能看到公共的内容,而管理员用户能看到一些内部的链接内容,这里借助Thymeleaf简单实现以下这个需求

pom引入相关依赖

   <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity5</artifactId>
   </dependency>

在需要进行控制的页面引入安全标签库

<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">

页面上使用安全标签

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>

<body>

<!--是否匿名访问(未登录)-->
    <div sec:authorize="isAnonymous()">
        <h2 align="center">游客你好,如果想进行操作<a th:href="@{/toLoginPage}">请登录</a> </h2>
    </div>

<!--是否进行身份认证(已登录)-->
    <div sec:authorize="isAuthenticated()">

		<!--sec:authentication="name":登录用户的用户名-->
        <h2 align="center">你好,<span sec:authentication="name"></span>,
            你拥有的权限:
		<!--sec:authentication="principal.authorities":登录用户的权限-->	
            <span sec:authentication="principal.authorities"></span>  
            ,你可进行如下操作</h2>
        <form th:action="@{/mylogout}" method="post">
            <input th:type="submit" th:value="注销" />
        </form>
    </div>

<!--根据不同的用户权限划分可显示的区域-->
    <!--用户拥有common权限才可展示-->  
    <div sec:authorize="hasAuthority('common')">
        <p><a th:href="@{/common/ArticleList}">查看列表</a></p>
    </div>

	<!--用户拥有admin权限才可展示--> 
    <div sec:authorize="hasAuthority('admin')">
        <p><a th:href="@{/admin/toUpload}">上传</a></p>
        <p><a th:href="@{/admin/toDownload}">下载</a></p>
    </div>
    
</body>
</html>

后端接口获取用户信息

同样,在后端也可以通过调用Security的接口,获取已登录的用户信息

/*获取用户信息*/
    @RequestMapping("/getUserInfo")
    @ResponseBody
    public String getUserInfo()
    {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        /*获取安全环境上下文*/
        SecurityContext context = SecurityContextHolder.getContext();

        Authentication authentication = context.getAuthentication();
        /*获取封装了用户信息的userDetails对象*/
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();

	//获取用户名
        String str="用户名:"+userDetails.getUsername();
        str = str+";用户权限:(";
        //遍历用户权限并拼接字符串
        for(GrantedAuthority authority:userDetails.getAuthorities())
        {

            System.out.println(authority.toString());
            str= str+ authority.toString()+",";
        }
        str=str+")";
        return str;
    }