本文讲述Spring Boot整合Spring Security在方法上使用注解实现权限控制,使用自定义UserDetailService,从MySQL中加载用户信息。使用Security自带的MD5加密,对用户密码进行加密。页面模板采用thymeleaf引擎。
源码地址:https://github.com/li5454yong/springboot-security.git
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来控制权限。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍SpringBoot整合UEditor的示例代码,包括了SpringBoot整合UEditor的示例代码的使用技巧和注意事项,需要的朋友参考一下 当前开发项目涉及到富文本框,了解了不少富文本编辑器之后,最终决定使用度娘的UEditor。原因:功能强大,并且自带适配java后端的图片和视频上传。 项目地址 不多说,上一下该项目的地址: http://ueditor.baidu.com/
本文向大家介绍springboot整合Mybatis、JPA、Redis的示例代码,包括了springboot整合Mybatis、JPA、Redis的示例代码的使用技巧和注意事项,需要的朋友参考一下 引言 在springboot 项目中,我们是用ORM 框架来操作数据库变的非常方便。下面我们分别整合mysql ,spring data jpa 以及redis 。让我们感受下快车道。 我们首先创建一
本文向大家介绍springBoot整合RocketMQ及坑的示例代码,包括了springBoot整合RocketMQ及坑的示例代码的使用技巧和注意事项,需要的朋友参考一下 版本: JDK:1.8 springBoot:1.5.10 rocketMQ:4.2.0 pom 配置: application.properties 配置: java代码: 生产者 消费者: 掉坑总结: 1.roc
本文向大家介绍Springboot整合Shiro的代码实例,包括了Springboot整合Shiro的代码实例的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了Springboot整合Shiro的代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1、导入依赖 2、创建ShiroRealm.java文件 (这里按照需求,只做登录认证
本文向大家介绍springboot整合 beatlsql的实例代码,包括了springboot整合 beatlsql的实例代码的使用技巧和注意事项,需要的朋友参考一下 BeetSql是一个全功能DAO工具, 同时具有hibernate 优点 & Mybatis优点功能,适用于承认以SQL为中心,同时又需求工具能自动能生成大量常用的SQL的应用。 beatlsql 优点 开发效率 无需注解,自动使用
本文向大家介绍SpringBoot整合Swagger2代码实例,包括了SpringBoot整合Swagger2代码实例的使用技巧和注意事项,需要的朋友参考一下 首先遵循SpringBoot的三板斧 第一步添加依赖 第二步添加注解 @EnableSwagger2 //启动SwaggerUI,在启动类或Swagger配置类上添加该注解 第三步写配置 扩展:swagger-bootstrap-ui是sp
本文向大家介绍springboot整合httpClient代码实例,包括了springboot整合httpClient代码实例的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了springboot整合httpClient代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 创建httpClientConfig配置类 创建HttpCli
本文向大家介绍springboot 整合 freemarker代码实例,包括了springboot 整合 freemarker代码实例的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了springboot 整合 freemarker代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 依赖 application.yml applic