首先我是一名大学生,最近了解到SpringSecurity,其功能非常强大,与SpringBoot完美整合,本文讲述Spring Boot2.x整合Spring Security在方法上使用注解实现权限控制,使用自定义UserDetailService,从MySQL中加载用户信息。使用Security推荐的BCrypt对用户的密码进行加密。页面模板采用thymeleaf引擎。
1. 引入依赖
1 2 3 4 5 6 |
compile('org.springframework.boot:spring-boot-starter-data-jpa') compile('org.springframework.boot:spring-boot-starter-security') compile('org.springframework.boot:spring-boot-starter-thymeleaf') compile('org.springframework.boot:spring-boot-starter-web') runtime('mysql:mysql-connector-java') compile 'com.alibaba:druid-spring-boot-starter:1.1.9' |
这里使用druid连接池,Spring Data Jpa做数据访问
2. Spring Securit配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
/** * @author 胡少 * 安全配置 */ @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true)//开启Security注解 public class SecurityConfig extends WebSecurityConfigurerAdapter { private static final String KEY="https://github.com/HuShao96"; @Autowired private UserDetailsService userDetailsService; @Autowired private PasswordEncoder passwordEncoder; /** * 自定义配置类 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http)throws Exception{ http.authorizeRequests() //静态文件允许你访问 .antMatchers("/css/**","/js/**","/fonts/**").permitAll() //所有的请求需要认证即登陆后才能访问 .anyRequest().authenticated() .and() //form表单验证 .formLogin().loginPage("/login") //设置默认登陆成功跳转的页面 .defaultSuccessUrl("/index") //登陆失败的请求 .failureUrl("/login-error").permitAll() .and() //开启cookie保存用户数据 .rememberMe() //设置cookie有效期 .tokenValiditySeconds(60 * 60 * 24 * 7) //设置cookie私钥 .key(KEY) //处理异常,拒绝访问就重定向403页面 .and().exceptionHandling().accessDeniedPage("/403"); } /** * 认证信息 * @param auth * @throws Exception */ @Autowired public void configureGlobal(AuthenticationManagerBuilder auth)throws Exception{ auth.userDetailsService(userDetailsService) //使用BCrypt加密 .passwordEncoder(new BCryptPasswordEncoder()); } |
这里做的配置有:
- 设置了登录url、登录成功和失败跳转的url以及异常跳转的url。
- 开启cookie保存用户数据并设置有效期以及私钥,这样前台页面提供name=”remember-me”的checkbox就可实现。
- 使用@EnableGlobalMethodSecurity(prePostEnabled = true)这个注解,可以开启security的注解,我们可以在需要控制权限的方法上面使用@PreAuthorize,@PreFilter这些注解。
3. 自定义userDetailService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UserRepository userRepository; @Autowired private RoleRepository roleRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { AimiUser user=userRepository.findByUsername(username); if(user!=null){ //获取权限,如果用户实体类实现了UserDetails接口的不需要此操作 List |
这里只需要实现UserDetailsService 接口,重写loadUserByUsername方法,从数据库中取出用户信息,并根据信息获取权限,最后返回一个UserDetails 实现类。
4. 定义UserDetails实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
public class SecurityUserDetails implements UserDetails { private String username; private String password; private List |
5. 定义Controller控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
@Controller public class TestController { @GetMapping("/index") public String index() { return "index"; } @PreAuthorize("hasRole('admin')") @GetMapping("/admin") public String admin(){ return "admin"; } @GetMapping("/login") public String login(){ return "login"; } @GetMapping("/") public String root() { return "index"; } /** * 登陆失败 * @return */ @GetMapping("/login-error") public String loginError(Model model){ model.addAttribute("loginError",true); model.addAttribute("errorMsg","登陆失败!"); return "login"; } @GetMapping("/403") public String error(Model model){ model.addAttribute("error","权限不够"); return "403"; } } |
在admin()方法上面使用了@PreAuthorize(“hasRole(‘admin’)”),表示访问这个方法需要拥有admin角色。如果想某指定某个角色能访问@PreAuthorize(“hasAnyRole(‘admin’,’user’)”),更多的使用方法可以去看官方文档。 需要注意的是,Spring Security默认的角色前缀是”ROLE_”,使用hasRole方法时已经默认加上了,因此我们在数据库里面的用户角色应该是“ROLE_admin”,在admin前面加上”ROLE_”前缀 。
6. 前台登录页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
注意:这里的input里的name=username,name=password,name=remember-me 以及post登录方法:”/login”都是固定的如要更改,Security配置文件也需要配置
7. 附加
如果访问除Get以外的方法可以会包403错误,原因是:框架内部防止CSRF(Cross-site request forgery跨站请求伪造)的发生,限制了除了get以外的大多数方法,解决办法如下:
- 首先在标签内添加如下内容
1 2
2. 然后在在ajax中添加代码如下:
1 2 3 |
var token = $('meta[name="_csrf"]').attr("content"); var header = $('meta[name="_csrf_hader"]').attr("content"); //接着在header请求头添加name=header,value=token; |
8. 总结
Spring Boot整合Spring Security大大简化了配置,使整个项目更安全!好了我要讲的到此为止!