spring mvc集成spring security

代码结构

总共涉及到7个类文件和添加角色配置文件以及需要在web.xml中添加一个过滤器(如果是spring boot的话则不需要配置web.xml)

mark


涉及文件及其作用简单分析

  1. role-config.properties:用来配置所有角色及所持权限信息
  2. Role.java:将配置文件中的权限信息分装到java类中,方便处理
  3. RoleHandler.java:构建Role对象列表,以便可以通过配置文件的key获取到相关Role对象
  4. ‘UserDtailsBean.java’:根绝具体业务需求来给用户对象添加(自定义)security需要的属性
  5. ‘UserDetailsServiceCustom’:自定义用户信息获取服务,主要是重写loadUserByUsername方法
  6. AuthenticationProviderCustom.java:自定义授权认证类,当用户登录,则会调用该方法,授权操作就是在这里进行的
  7. CustomSuccessHandler:当登录成功后的处理操作,可以省去或直接跳转到某个页面
  8. SecurityConfig:Security的核心配置类,在这里使用了自定义的AuthenticationProviderCustom和AuthenticationProviderCustom进行用户信息的构建以及授权类,具体配置内容可参考官网(当然,也可以用XML进行配置,而不是Java Config的形式)
  9. web.xml:配置security过滤器(使用spring boot的话无须配置)

具体代码案例

1.在配置文件中配置好各个角色的信息,security会默认解析以ROLE_开头的权限内容作为角色,例如ROLE_ADMIN,则将作为ADMIN角色

mark

2.添加角色Bean对象

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
import java.util.List;

public class Role {
private String name; // 名称
private List<String> privileges; // 分配的角色/权限

Role(){

}

Role(String name, List<String> privileges){
this.name = name;
this.privileges = privileges;
}

List<String> getPrivileges(){
return this.privileges;
}

String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

3.提供角色权限初始化类

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import com.x.x.common.UserType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
* 角色权限管理以及初始化
* 目前是读取配置文件来初始化用户权限
* <p>
* Created by surpass.wei@gmail.com on 2017/3/6.
*/
@Component
public class RoleHandler {

@Value("${role.admin}")
private String adminRoleStr;

@Value("${role.teacher}")
private String teacherRoleStr;

@Value("${role.trainee}")
private String traineeRoleStr;

private final List<Role> roleList = new ArrayList<>();

/**
* 根据config.properties配置文件中的角色权限控制来初始化roles
*/
private void init() {
UserType[] userTypes = UserType.values();

Role role;
List<String> privilegeList;
/**
* 循环角色类型获取相应的配置字符串,根据“,”分隔
* 构建Role的List
*/
for (UserType userType : userTypes) {

String currentRoleStr; // 当前角色的权限信息
if (userType.equals(UserType.ADMIN)) {
currentRoleStr = adminRoleStr;
} else if (userType.equals(UserType.TEACHER)) {
currentRoleStr = teacherRoleStr;
} else if (userType.equals(UserType.TRAINEE)) {
currentRoleStr = traineeRoleStr;
} else {
continue;
}
if (currentRoleStr.isEmpty()) {
return;
}

privilegeList = new ArrayList<>();
String[] roleArr = currentRoleStr.split(",");
for (String s : roleArr) {
privilegeList.add(s.trim());
}
role = new Role(userType.getRoleName(), privilegeList);

roleList.add(role);
}

}

public Role getRole(String roleName) {
if (roleList.isEmpty()) {
this.init();
}

if (roleName == null || roleName.isEmpty()) {
return null;
}

Role role = new Role();
for (Role temp_role : roleList) {
if (temp_role.getName().compareToIgnoreCase(roleName) == 0) {
role = temp_role;
}
}

return role;
}
}

4.封装UserDetailBean

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
72
73
74
75
76
77
78
79
80
import com.x.x.common.UserType;
import com.x.x.entity.User;
import com.x.x.util.BeanUtil;
import com.x.x.util.SpringContextUtil;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;

/**
* 基于User包装一个UserDetailsBean(用于security权限认证)
* 主要为了实现 getAuthorities()
*/
public class UserDetailsBean extends User implements UserDetails {

public UserDetailsBean(User user) {
BeanUtil.copyObjValue(user, this);
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 获取用户的角色/权限信息

Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
RoleHandler roleHandler = SpringContextUtil.getApplicationContext().getBean(RoleHandler.class);

if (roleHandler == null) {
return null;
}

// 根据用户的类型, 构建用户对应的Role
String roleName;

roleName = UserType.getRoleName(this.getUserType());
Role role = roleHandler.getRole(roleName);

// 循环角色的权限信息, 构建authorities
for (String privilege : role.getPrivileges()) {
authorities.add(new SimpleGrantedAuthority(privilege));
}

return authorities;
}

@Override
public String getPassword() {
return this.getPwd();
}

@Override
public String getUsername() {
return this.getLoginName();
}

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

@Override
public boolean isAccountNonLocked() {
// 账户是否未锁定
return this.getUserState() == 0;
}

@Override
public boolean isCredentialsNonExpired() {
// 凭证是否未过期
return true;
}

@Override
public boolean isEnabled() {
// 是否有效
return true;
}
}

5.实现UserDetailsService重写loadUserByUsername方法

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
import com.x.x.entity.User;
import com.x.x.service.IUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
* 重写loadUserByUsername
* Created by surpass.wei@gmail.com on 2016/12/16.
*/
@Component
public class UserDetailsServiceCustom implements UserDetailsService {
private Logger logger = LoggerFactory.getLogger(UserDetailsServiceCustom.class);

@Resource
private IUserService userService;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

UserDetailsBean userDetailsBean;

User search = new User();
search.setLoginName(username);
User user = userService.findOneByEntity(search);

if (user==null) {
logger.warn("该用户不存在:" + username);
throw new UsernameNotFoundException("该用户不存在:" + username);
} else {
userDetailsBean = new UserDetailsBean(user);
}

return userDetailsBean;
}
}

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import com.x.x.entity.User;
import com.x.x.service.IUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class AuthenticationProviderCustom implements AuthenticationProvider {
private Logger logger = LoggerFactory.getLogger(AuthenticationProviderCustom.class);

@Resource
private IUserService userService;

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
String loginName = token.getName();
String password = token.getCredentials().toString();
User user = userService.getUserByLoginNameAndPwd(loginName, password);

UserDetailsBean userDetailsBean;
if(user != null) {
userDetailsBean = new UserDetailsBean(user);
} else {
logger.error("用户名或密码错误");
throw new BadCredentialsException("用户名或密码错误");
}

return new UsernamePasswordAuthenticationToken(userDetailsBean, password, userDetailsBean.getAuthorities());
}

@Override
public boolean supports(Class<?> aClass) {
return true;
}
}

7.配置Security

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
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

import javax.annotation.Resource;


@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity //这里如果放开js文件将不被允许执行,注释掉也没有影响,不知道为什么
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Resource
private CustomSuccessHandler customSuccessHandler;

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").permitAll()
//.antMatchers("/static/**").permitAll() // 允许所有用户对根路径以及匹配"/static/"开头的路径的访问
//.antMatchers("/", "/index").hasRole("TEACHER")
.anyRequest().authenticated() // 任何尚未匹配的的URL地址只需要对用户进行权限验证
.and()
.formLogin()
.successHandler(customSuccessHandler)
.failureUrl("/login?error=true")
//.defaultSuccessUrl("/home")
//.defaultSuccessUrl("/swagger-ui.html") // 登陆成功后默认默认跳转到swagger页
.permitAll()
.and()
.rememberMe().tokenValiditySeconds(604800);
http.logout();
http.csrf().disable();
}

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// 将验证过程交给自定义的授权认证类
auth.authenticationProvider(authenticationProvider());
}

@Bean
@Override
protected UserDetailsService userDetailsService() {
return new UserDetailsServiceCustom();
}

@Bean
public AuthenticationProvider authenticationProvider() {
return new AuthenticationProviderCustom();
}
}

8.自定义成功处理类(可选)

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
import org.springframework.security.core.Authentication;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@Component
public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

@Override
protected void handle(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
UserDetailsBean user = (UserDetailsBean) authentication.getPrincipal();

String targetUrl;

if (UserType.ADMIN.getValue().equals(user.getUserType())) {
targetUrl = "/swagger-ui.html";
} else if (UserType.TEACHER.getValue().equals(user.getUserType())) {
targetUrl = "/teacher";
} else if (UserType.TRAINEE.getValue().equals(user.getUserType())){
targetUrl = "/trainee";
} else {
targetUrl = "/login?error";
}

redirectStrategy.sendRedirect(request, response, targetUrl);
}
}

9.添加security过滤器

1
2
3
4
5
6
7
8
9
10
<!--spring security-->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>