SpringBoot与SpringSecurity整合的初使用
1.背景:
最近的一个项目中,需要做web页面的管理后台,需要对不同角色进行不同的管理,特此研究了一下SpringSecurity的使用。
2.正题:
1.采用框架:springboot,springsecurity,jpa
2.采用idea的spring initializr 初始化一个springboot项目,勾选模块为web,securiy,和jpa,jdbc。
3.表结构:
总共有三张表,分别为user,user_authorities,authentication
user表
authentication表
user_authorities表:
4.代码开始:
4.1 首先,创建jpa对应的实体类:
这个就不贴代码了,根据上面的表结构创建对应的类,加上jpa对应的注解即可
4.2 创建Repository类,继承JpaRepository
4.3 创建SpringSecurity配置类WebSecurityConfigurerAdapter:
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailService userDetailsService;
@Autowired
private AuthenticationRepository authenticationRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
//super.configure(http);
http.csrf().disable();
//定义请求规则,查询数据库,将authentication表中的请求规则查询配置
List<Authentication> authentications = authenticationRepository.findAll();
http.authorizeRequests()
.antMatchers("/").permitAll();
for(Authentication authentication :authentications){
http.authorizeRequests().
antMatchers(authentication.getDescription())
.hasRole(authentication.getName().
substring(authentication.getName().indexOf("ROLE_")+5));
}
//开启自动配置的登录功能
http.formLogin();
//1./login请求来到登陆页面
//2.重定向到/login?error 表示登录失败
//3.
//开启自动注销功能
//访问logout表示注销,清空session
//注销成功会默认返回/login?logout页面
//可以通过配置logoutSuccessUrl来配置注销成功的返回地址
http.logout().logoutSuccessUrl("/");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
public DaoAuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder(8));
authenticationProvider.setUserDetailsService(userDetailsService);
return authenticationProvider;
}
}
4.4 配置 AccessDecisionManager类
@Service
public class MyAccessDecisionManager implements AccessDecisionManager {
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
if (configAttributes == null ) {
throw new AccessDeniedException("对不起,您没有此权限");
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(new Date())+":\t"+object.toString());
System.out.println("configAttributes=="+configAttributes);
System.out.println("authentication=="+authentication.getAuthorities());
for(ConfigAttribute ca:configAttributes){
String needRole = ca.getAttribute();
for(GrantedAuthority userGA:authentication.getAuthorities()) {
if(needRole.equals(userGA.getAuthority())) { // ga is user's role.
return ;
}
}
}
throw new AccessDeniedException("对不起,您没有此权限");
}
public boolean supports(ConfigAttribute arg0) {
// TODO Auto-generated method stub
return true;
}
public boolean supports(Class<?> arg0) {
// TODO Auto-generated method stub
return true;
}
}
4.5 实现一个继承userDetailService的类,根据业务逻辑实现loadUserByUserName
@Service
public class MyUserDetailService implements UserDetailsService {
@Autowired
private UsersRepository userRepository;
@Autowired
private UserAuthoritiesRepository userAuthoritiesRepository;
@Override
@Transactional
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userRepository.findByUsername(s);
if(user == null){
throw new UsernameNotFoundException(s);
}
List<GrantedAuthority> authorities = new ArrayList<>();
List<UserAuthorities> userAuthorities = userAuthoritiesRepository.findAllByUser(user);
for(UserAuthorities userAuthorities1:userAuthorities){
authorities.add(new SimpleGrantedAuthority(userAuthorities1.getAuthentication().getName()));
}
return new MyUserPrincipal(user,authorities);
}
}
4.6 实现AbstractSecurityInterceptor,继承Filter
@Service
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
@Autowired
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Autowired
public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
super.setAccessDecisionManager(myAccessDecisionManager);
}
public FilterInvocationSecurityMetadataSource getSecurityMetadataSource(){
return this.securityMetadataSource;
}
@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 Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
}
4.7 实现FilterInvocationSecurityMetadataSource
@Service
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {
private HashMap<String, Collection<ConfigAttribute>> map = null;
@Autowired
private AuthenticationRepository authenticationRepository;
/**
* 加载权限表中所有权限,这里不想从数据库中获取直接写在了这
*/
public void loadResourceDefine() {
map = new HashMap<>();
Collection<ConfigAttribute> array;
// ConfigAttribute cfg, cfg1,cfg2;
array = new ArrayList<>();
List<Authentication> authentications = authenticationRepository.findAll();
for(Authentication authentication:authentications){
System.out.println(authentication);
ConfigAttribute cfg = new SecurityConfig(authentication.getName());
array.add(cfg);
map.put(authentication.getDescription(),array);
}
}
//此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,
// 用来判定用户是否有此权限。如果不在权限表中则放行。
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
System.out.println("object的类型为:" + object.getClass());
FilterInvocation filterInvocation = (FilterInvocation) object;
String url = filterInvocation.getRequestUrl();
System.out.println("访问的URL地址为(包括参数):" + url);
url = filterInvocation.getRequest().getServletPath();
System.out.println("访问的URL地址为:" + url);
if (map == null)
loadResourceDefine();
//object 中包含用户请求的request 信息
final 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);
//matches() 方法用于检测字符串是否匹配给定的正则表达式
boolean a = matcher.matches(request);
if (matcher.matches(request)) {
Collection<ConfigAttribute> c = map.get(resUrl);
return map.get(resUrl);
}
}
return null;
// return collection;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
//UsernamePasswordAuthenticationToken.class.equals(clazz);
return FilterInvocation.class.isAssignableFrom(clazz);
//return true;
}
}
来源:oschina
链接:https://my.oschina.net/u/4352976/blog/3628309