SpringBoot2.x+SpringSecurity(一)安全配置整合

首先我是一名大学生,最近了解到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());
    }

这里做的配置有:

  1. 设置了登录url、登录成功和失败跳转的url以及异常跳转的url。
  2. 开启cookie保存用户数据并设置有效期以及私钥,这样前台页面提供name=”remember-me”的checkbox就可实现。
  3. 使用@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 roles=roleRepository.listRoleByUser(user.getId());

            return new SecurityUserDetails(user,roles);
        }
        return null;
    }
}

这里只需要实现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 roles;

    public SecurityUserDetails(AimiUser user, List roles) {
        this.username = user.getUsername();
        this.password = user.getPassword();
        this.roles = roles;
    }

    @Override
    public Collection getAuthorities() {
        List authorities=new ArrayList<>();
        List roles=this.roles;
        for(Role role:roles){
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }


    /**
     * 是否过期
     * @return
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * 是否被锁定
     * @return
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * 是否过期
     * @return
     */

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * 是否可用
     * @return
     */

    @Override
    public boolean isEnabled() {
        return true;
    }
}

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. 首先在标签内添加如下内容
    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大大简化了配置,使整个项目更安全!好了我要讲的到此为止!

转载于:https://my.oschina.net/u/4037000/blog/3045346

你可能感兴趣的:(SpringBoot2.x+SpringSecurity(一)安全配置整合)