用户实体类
参考:springboot+springsecurity基于角色的权限验证(二)
配置类
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled =true) // 启用授权注解
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
/** 获取数据库中信息存到User对象中 */
@Bean
public UserDetailsService userService(){ //注册userService 的bean
return new UserServiceImpl();
}
/**加密密码*/
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**MD5加密密码*/
@Bean
PasswordEncoder md5PasswordEncoder() {
return new MD5PasswordEncoder();
}
/** 放行静态资源 */
@Override
public void configure(WebSecurity web) throws Exception {
//解决静态资源被拦截的问题
web.ignoring().antMatchers("/css/**");
web.ignoring().antMatchers("/js/**");
web.ignoring().antMatchers("/images/**");
web.ignoring().antMatchers("/login/**");
//解决服务注册url被拦截的问题
web.ignoring().antMatchers("/resources/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 登录
http.formLogin().loginPage("/toLogin").loginProcessingUrl("/doLogin")
.defaultSuccessUrl("/index")
.failureUrl("/toLogin?error=true");
//解决非thymeleaf的form表单提交被拦截问题
http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/toLogin")
.permitAll()
.anyRequest()
.authenticated()
;
//session管理
//session失效后跳转到登录页面
http.sessionManagement().invalidSessionUrl("/toLogin");
//单用户登录,如果有一个登录了,同一个用户在其他地方登录将前一个剔除下线
//http.sessionManagement().maximumSessions(1).expiredSessionStrategy(expiredSessionStrategy());
//单用户登录,如果有一个登录了,同一个用户在其他地方不能登录
http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true);
//退出删除cookie
http.logout()
.permitAll()
.logoutUrl("/logout") //执行注销的url
.invalidateHttpSession(true) // 指定是否在注销时让httpSession无效
.deleteCookies("JESSIONID") // 清除cookie
.logoutSuccessUrl("/toLogin"); // 注销成功后跳转的url
super.configure(http);
//解决中文乱码问题
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
//http.addFilterBefore(filter,CsrfFilter.class);
http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
}
/**
* 认证器
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 自定义加密
auth.userDetailsService(userService()).passwordEncoder(md5PasswordEncoder());
}
}
创建决策类 MyAccessDecisionManager
/**
* 决策管理器(授权)
*
* @ClassName: MyAccessDecisionManager
* @description
* @author Jayden
* @createDate 2019年4月8日-下午2:02:32
*/
@Service
public class MyAccessDecisionManager implements AccessDecisionManager {
/**
* decide 方法是判定是否拥有权限的决策方法,
* authentication 是释UserService中循环添加到 GrantedAuthority 对象中的权限信息集合.
* object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
*
* configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,
* 此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
*
*/
@Override
public void decide(Authentication authentication, Object obj, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if(null== configAttributes || configAttributes.size() <= 0) {
return;
}
ConfigAttribute c;
String needRole;
for(Iterator<ConfigAttribute> iter = configAttributes.iterator(); iter.hasNext(); ) {
c = iter.next();
needRole = c.getAttribute();
//authentication 为在注释1 中循环添加到 GrantedAuthority 对象中的权限信息集合
for(GrantedAuthority ga : authentication.getAuthorities()) {
if(needRole.trim().equals(ga.getAuthority())) {
return;
}
}
}
throw new AccessDeniedException("no right");
}
@Override
public boolean supports(ConfigAttribute configattribute) {
// TODO Auto-generated method stub
return true;
}
@Override
public boolean supports(Class<?> class1) {
// TODO Auto-generated method stub
return true;
}
}
创建类 MyFilterSecurityInterceptor
@Service
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
@Autowired
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Autowired
public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
super.setAccessDecisionManager(myAccessDecisionManager);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
// fi里面有一个被拦截的url
// 里面调用MyInvocationSecurityMetadataSource的getAttributes(Object
// object)这个方法获取fi对应的所有权限
// 再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
// 执行下一个拦截器
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public Class<?> getSecureObjectClass() {
// TODO Auto-generated method stub
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
// TODO Auto-generated method stub
return this.securityMetadataSource;
}
}
创建类 MyInvocationSecurityMetadataSourceService
@Service
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {
@Autowired
private T_permissionMapper permissionMapper;
private HashMap<String, Collection<ConfigAttribute>> map =null;
/**
* 加载权限表中所有权限
*/
public void loadResourceDefine(){
map = new HashMap<>();
Collection<ConfigAttribute> array;
ConfigAttribute cfg;
// 查询到权限表所有信息
List<T_permission> permissions = permissionMapper.findAll();
for(T_permission permission : permissions) {
array = new ArrayList<>();
cfg = new SecurityConfig(permission.getName());
/**
*此处只添加了用户的名字,其实还可以添加更多权限的信息,例如请求方法到ConfigAttribute的集合中去。
*此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
*/
array.add(cfg);
//用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
map.put(permission.getUrl(), array);
}
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
// TODO Auto-generated method stub
return null;
}
/**
* 此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,
* 用来判定用户是否有此权限。如果不在权限表中则放行。
*/
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
if(map ==null) loadResourceDefine();
//object 中包含用户请求的request 信息
HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
AntPathRequestMatcher matcher;
String resUrl;
for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
resUrl = iter.next();
matcher = new AntPathRequestMatcher(resUrl);
if(matcher.matches(request)) {
return map.get(resUrl);
}
}
return null;
}
@Override
public boolean supports(Class<?> class1) {
// TODO Auto-generated method stub
return true;
}
}
权限 PermissionMapper.xml
用户表,角色表,权限表,用户角色中间表,角色权限中间表
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jayden.mapper.T_permissionMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.jayden.entity.T_permission">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="description" property="description" />
<result column="url" property="url" />
<result column="pid" property="pid" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, name, description, url, pid
</sql>
<!-- 通过userid查询权限信息 -->
<select id="selectPermsByUserId" resultMap="BaseResultMap">
select p.*
from t_user u
LEFT JOIN t_user_role tur on u.id = tur.user_id
LEFT JOIN t_role r on tur.role_id = r.id
LEFT JOIN t_role_permission trp on trp.role_id = r.id
LEFT JOIN t_permission p on p.id = trp.permission_id
where u.id=#{userId}
</select>
<!-- 查询所有权限 -->
<select id="findAll" resultMap="BaseResultMap">
select * from t_permission
</select>
UserDetailsService 实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, T_user> implements UserService, UserDetailsService {
// 用户
@Autowired
private UserMapper userMapper;
// 权限
@Autowired
private T_permissionMapper permissionMapper;
// 加密
@Autowired
private MD5PasswordEncoder md5PasswordEncoder;
/**
* 重写springSecurity类方法
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 通过用户名称获取对象
T_user user = userMapper.findByUserName(username);
if (user != null) {
// 因为数据库中密码是明文的,所以在这里加密了(仅测试使用:123为盐)
user.setPassword(md5PasswordEncoder.encode(user.getPassword()+"123"));
// 通过用户id获取权限信息
List<T_permission> permissions = permissionMapper.selectPermsByUserId(user.getId());
List<GrantedAuthority> grantedAuthorities = new ArrayList <>();
for (T_permission permission : permissions) {
if (permission != null && permission.getName() !=null) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getName());
//此处将权限信息添加到 GrantedAuthority 对象中,在后面进行全权限验证时会使用GrantedAuthority 对象
grantedAuthorities.add(grantedAuthority);
}
}
return new User(user.getUsername(), user.getPassword(), grantedAuthorities);
} else {
System.out.println("该用户不存在");
throw new UsernameNotFoundException("用户: " + username + " 不存在!");
}
}
}