文章首發(fā) http://surpass.esy.es
代碼結(jié)構(gòu)
總共涉及到7個類文件
和添加角色配置文件
以及需要在web.xml
中添加一個過濾器(如果是spring boot的話則不需要配置web.xml)
涉及文件及其作用簡單分析
role-config.properties
:用來配置所有角色及所持權(quán)限信息Role.java
:將配置文件中的權(quán)限信息分裝到j(luò)ava類中,方便處理RoleHandler.java
:構(gòu)建Role對象列表齿诞,以便可以通過配置文件的key獲取到相關(guān)Role對象- 'UserDtailsBean.java':根絕具體業(yè)務需求來給用戶對象添加(自定義)security需要的屬性
- 'UserDetailsServiceCustom':自定義用戶信息獲取服務白对,主要是重寫loadUserByUsername方法
AuthenticationProviderCustom.java
:自定義授權(quán)認證類界酒,當用戶登錄,則會調(diào)用該方法,授權(quán)操作就是在這里進行的CustomSuccessHandler
:當?shù)卿洺晒蟮奶幚聿僮鳎梢允∪セ蛑苯犹D(zhuǎn)到某個頁面SecurityConfig
:Security的核心配置類楼吃,在這里使用了自定義的AuthenticationProviderCustom和AuthenticationProviderCustom進行用戶信息的構(gòu)建以及授權(quán)類,具體配置內(nèi)容可參考官網(wǎng)(當然,也可以用XML進行配置妄讯,而不是Java Config的形式)web.xml
:配置security過濾器(使用spring boot的話無須配置)
具體代碼案例
1.在配置文件中配置好各個角色的信息孩锡,security會默認解析以ROLE_
開頭的權(quán)限內(nèi)容作為角色,例如ROLE_ADMIN
亥贸,則將作為ADMIN
角色
2.添加角色Bean對象
import java.util.List;
public class Role {
private String name; // 名稱
private List<String> privileges; // 分配的角色/權(quán)限
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.提供角色權(quán)限初始化類
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;
/**
* 角色權(quán)限管理以及初始化
* 目前是讀取配置文件來初始化用戶權(quán)限
* <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<>();
/**
* 根據(jù)config.properties配置文件中的角色權(quán)限控制來初始化roles
*/
private void init() {
UserType[] userTypes = UserType.values();
Role role;
List<String> privilegeList;
/**
* 循環(huán)角色類型獲取相應的配置字符串躬窜,根據(jù)“,”分隔
* 構(gòu)建Role的List
*/
for (UserType userType : userTypes) {
String currentRoleStr; // 當前角色的權(quán)限信息
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
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權(quán)限認證)
* 主要為了實現(xiàn) getAuthorities()
*/
public class UserDetailsBean extends User implements UserDetails {
public UserDetailsBean(User user) {
BeanUtil.copyObjValue(user, this);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 獲取用戶的角色/權(quán)限信息
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
RoleHandler roleHandler = SpringContextUtil.getApplicationContext().getBean(RoleHandler.class);
if (roleHandler == null) {
return null;
}
// 根據(jù)用戶的類型, 構(gòu)建用戶對應的Role
String roleName;
roleName = UserType.getRoleName(this.getUserType());
Role role = roleHandler.getRole(roleName);
// 循環(huán)角色的權(quán)限信息, 構(gòu)建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.實現(xiàn)UserDetailsService重寫loadUserByUsername方法
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.自定義的授權(quán)認證類
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
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文件將不被允許執(zhí)行,注釋掉也沒有影響炕置,不知道為什么
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地址只需要對用戶進行權(quán)限驗證
.and()
.formLogin()
.successHandler(customSuccessHandler)
.failureUrl("/login?error=true")
//.defaultSuccessUrl("/home")
//.defaultSuccessUrl("/swagger-ui.html") // 登陸成功后默認默認跳轉(zhuǎn)到swagger頁
.permitAll()
.and()
.rememberMe().tokenValiditySeconds(604800);
http.logout();
http.csrf().disable();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// 將驗證過程交給自定義的授權(quán)認證類
auth.authenticationProvider(authenticationProvider());
}
@Bean
@Override
protected UserDetailsService userDetailsService() {
return new UserDetailsServiceCustom();
}
@Bean
public AuthenticationProvider authenticationProvider() {
return new AuthenticationProviderCustom();
}
}
8.自定義成功處理類(可選
)
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過濾器
<!--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>