Spring Boot整合Spring Security的示例代码
本文讲述spring boot整合spring security在方法上使用注解实现权限控制,使用自定义userdetailservice,从mysql中加载用户信息。使用security自带的md5加密,对用户密码进行加密。页面模板采用thymeleaf引擎。
源码地址:
1、引入pom依赖
<parent> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-parent</artifactid> <version>1.4.4.release</version> </parent> <dependencies> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-security</artifactid> </dependency> <dependency> <groupid>org.springframework.security.oauth</groupid> <artifactid>spring-security-oauth2</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-thymeleaf</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-data-jpa</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-jdbc</artifactid> </dependency> <dependency> <groupid>mysql</groupid> <artifactid>mysql-connector-java</artifactid> <version>5.1.34</version> </dependency> <dependency> <groupid>com.alibaba</groupid> <artifactid>druid</artifactid> <version>1.0.15</version> </dependency> </dependencies>
这里使用druid连接池,spring data jpa实现数据库访问。
2、配置spring security
@configuration @enablewebmvcsecurity @enableglobalmethodsecurity(prepostenabled = true)//开启security注解 public class websecurityconfig extends websecurityconfigureradapter { @bean @override protected authenticationmanager authenticationmanager() throws exception { return super.authenticationmanager(); } @override protected void configure(httpsecurity http) throws exception { //允许所有用户访问"/"和"/home" http.authorizerequests() .antmatchers("/", "/home").permitall() //其他地址的访问均需验证权限 .anyrequest().authenticated() .and() .formlogin() //指定登录页是"/login" .loginpage("/login") .defaultsuccessurl("/hello")//登录成功后默认跳转到"/hello" .permitall() .and() .logout() .logoutsuccessurl("/home")//退出登录后的默认url是"/home" .permitall(); } @autowired public void configureglobal(authenticationmanagerbuilder auth) throws exception { auth .userdetailsservice(customuserdetailsservice()) .passwordencoder(passwordencoder()); } /** * 设置用户密码的加密方式为md5加密 * @return */ @bean public md5passwordencoder passwordencoder() { return new md5passwordencoder(); } /** * 自定义userdetailsservice,从数据库中读取用户信息 * @return */ @bean public customuserdetailsservice customuserdetailsservice(){ return new customuserdetailsservice(); } }
这里只做了基本的配置,设置了登录url、登录成功后跳转的url、退出后跳转到的url。使用@enableglobalmethodsecurity(prepostenabled = true)这个注解,可以开启security的注解,我们可以在需要控制权限的方法上面使用@preauthorize,@prefilter这些注解。
3、自定义userdetailservice
public class customuserdetailsservice implements userdetailsservice { @autowired //数据库服务类 private suserservice suserservice; @override public userdetails loaduserbyusername(string username) throws usernamenotfoundexception { //suser对应数据库中的用户表,是最终存储用户和密码的表,可自定义 //本例使用suser中的email作为用户名: suser user = suserservice.finduserbyemail(username); if (user == null) { throw new usernamenotfoundexception("username " + username + " not found"); } // securityuser实现userdetails并将suser的email映射为username securityuser securityuser = new securityuser(user); collection<simplegrantedauthority> authorities = new arraylist<simplegrantedauthority>(); authorities.add(new simplegrantedauthority("role_admin")); return securityuser; } }
这里只需要实现userdetailsservice 接口,重写loaduserbyusername方法,从数据库中取出用户信息。最后返回一个userdetails 实现类。
4、定义错误处理配置
@configuration public class errorpageconfig { @bean public embeddedservletcontainercustomizer embeddedservletcontainercustomizer(){ return new mycustomizer(); } private static class mycustomizer implements embeddedservletcontainercustomizer { @override public void customize(configurableembeddedservletcontainer container) { container.adderrorpages(new errorpage(httpstatus.forbidden, "/403")); } } }
访问发生错误时,跳转到”/403”.
5、controller接口
@controller public class indexcontroller { @resource private suserservice suserservice; @requestmapping("/home") public string home() { return "home"; } @preauthorize("hasrole('user')") @requestmapping(value = "/admin",method = requestmethod.get) public string toadmin(){ return "helloadmin"; } @requestmapping("/hello") public string hello() { return "hello"; } @requestmapping("/login") public string login(){ return "login"; } @requestmapping("/") public string root() { return "index"; } @requestmapping("/403") public string error(){ return "403"; } }
在toadmin()方法上面使用了@preauthorize(“hasrole(‘user')”),表示访问这个方法需要拥有user角色。如果希望控制到权限层面,可以使用@preauthorize(“haspermission()”)。这里只是用了其中的一个用法,更多的使用方法可以去看官方文档。需要注意的是,spring security默认的角色前缀是”role_”,使用hasrole方法时已经默认加上了,因此我们在数据库里面的用户角色应该是“role_user”,在user前面加上”role_”前缀。
6、测试
启动项目,访问http://localhost:1130/login
点击登录后进入到“/hello”
点击跳转到管理员页面
在后台的“/admin”这个url对应的方法上面,限制了用户必须要拥有“user”角色。在数据库中也设置了登录的用户有这个角色。
现在我们修改数据库中的用户角色,改为“role_admin”。退出登录后重新登录,再次点击“前往管理员页面”按钮,会跳转到如下页面。
因为现在没有了“user”权限,所以访问的时候抛出了异常,被拦截后重定向到了“/403”。
7、post方式访问,错误码403
首先,把“/admin”改为post请求
@preauthorize("hasrole('user')") @requestmapping(value = "/admin",method = requestmethod.post) public string toadmin(){ return "helloadmin"; }
把“前往管理员页面”按钮的请求方式从原来的form表达get提交,改为ajax方式post提交。至于为什么不是用form的post提交后面再讲。先修改代码
<body> <h1 th:inline="text">hello [[${#httpservletrequest.remoteuser}]]!</h1> <!--<form th:action="@{/logout}" method="post"> <input type="submit" value="sign out"/> </form> <form th:action="@{/admin}" method="get"> <input th:type="submit" th:value="前往管理员页面"/> </form>--> <a th:href="@{/admin}" rel="external nofollow" >前往管理员用户页面</a> <input th:type="submit" onclick="testpost()" th:value="前往管理员页面"/> </body> <script> function testpost() { $.ajax({ url:"/admin", type:'post', success:function (data) { } }); } </script>
点击“前往管理员页面”按钮,在调试台可以看到如下
这是因为框架内部防止csrf(cross-site request forgery跨站请求伪造)的发生,限制了除了get以外的大多数方法。
下面说解决办法:
首先在标签内添加如下内容。
<meta name="_csrf" th:content="${_csrf.token}"/> <meta name="_csrf_hader" th:content="${_csrf.headername}"/>
只要添加这个token,后台就会验证这个token的正确性,如果正确,则接受post访问。
然后在ajax代码中添加以下代码:
var token = $('meta[name="_csrf"]').attr("content"); var header = $('meta[name="_csrf_hader"]').attr("content"); $(document).ajaxsend(function(e,xhr,opt){ xhr.setrequestheader(header,token); });
这样就可以正常使用post、delete等其他方式来访问了。
上面说到使用表单的post方式来提交,通过查看页面源代码可以看到
框架在form表单中自动插入了一个隐藏域,value值就是那个token,所以使用form表单来提交post请求是可以直接通过的,而ajax方式提交的话,需要加上那段代码。
好了,这篇文章就讲到这,后面还会有文章讲述rest api风格如何来使用spring security来控制权限。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: DDL、DML和DCL的区别与理解
推荐阅读
-
Spring Boot集成Kafka的示例代码
-
Spring Boot整合Spring Security的示例代码
-
spring Boot环境下dubbo+zookeeper的一个基础讲解与示例
-
spring security4 添加验证码的示例代码
-
spring boot整合Cucumber(BDD)的方法
-
使用Spring Boot集成FastDFS的示例代码
-
spring boot aop 记录方法执行时间代码示例
-
Spring整合Quartz分布式调度的示例代码
-
十、Spring boot 简单优雅的整合 Swagger2
-
Spring Boot Gradle发布war到tomcat的方法示例