Spring-Boot (四) 集成Spring Security
为什么80%的码农都做不了架构师?>>>
http://nealma.com/2016/04/30/spring-boot-4-security/
### 前言
shiro可以完全解决有关安全的问题,但是项目大多使用的是Spring框架,所以没必要在引入其他依赖,
so,还是折腾下亲儿子的东东吧
开发环境:
OS: Mac 10.11.6
IDE: IDEA
Build: Maven
以前都是使用xml来配置,但是spring-boot提倡Java Config的使用,
折腾下来还是很多收获的。有关权限管理,行内大都秉承
以角色为基础的访问控制(Role-based access control, RBAC)
在Spring Security中,实现原理很简单,通过AOP对所要管理的资源(url或者method)进行拦截,
在其内部维护了一条安全过滤链,有用户服务(UserDetailsService)、身份认证服务(AuthenticationProvider)、
访问决策管理(AccessDecisionManager)、记住我(remember-me)等普世功能。
当然,使用Spring-boot离不开Java Config,come on,just do it.
### WebSecurityConfig
```java
@Configuration
@ComponentScan("com.caogen")
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
@PropertySource("classpath:application.properties")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected final Logger LOGGER = LoggerFactory.getLogger(getClass());
@Autowired
private MyAuthenticationProvider authenticationProvider;//自定义验证
@Autowired
private MyUserDetailsService userDetailsService;
@Autowired
private MyPersistentTokenRepository tokenRepository;
@Autowired
private MyAccessDecisionManager accessDecisionManager;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
auth.userDetailsService(userDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.headers()
.frameOptions().sameOrigin().disable()//disable X-Frame-Options
.authorizeRequests()
// .accessDecisionManager(accessDecisionManager)//用注解替换,如果不使用注解,取消注释
.anyRequest().fullyAuthenticated()//其他url需要鉴权
.and()
.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/login")
.loginPage("/login")
.failureUrl("/login?error")
.permitAll()
.and()
.logout()
.deleteCookies("JSESSIONID")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login")
.and()
.rememberMe()
.tokenRepository(tokenRepository)
.rememberMeServices(rememberMeServices())
.rememberMeParameter("remember-me").key("key")
.tokenValiditySeconds(86400)
.and()
.csrf().disable() //disable csrf
.sessionManagement().maximumSessions(1);
}
@Bean
public RememberMeServices rememberMeServices() {
// Key must be equal to rememberMe().key()
PersistentTokenBasedRememberMeServices rememberMeServices =
new PersistentTokenBasedRememberMeServices("key", userDetailsService, tokenRepository);
rememberMeServices.setCookieName("remember-me");
rememberMeServices.setParameter("remember-me");
rememberMeServices.setTokenValiditySeconds(864000);
return rememberMeServices;
}
}
```
### MyUserDetailsService
```java
/**
* SPRING SECURITY用户登录处理
*/
@Service("MyUserDetailsServiceImpl")
public class MyUserDetailsService implements UserDetailsService {
protected final Logger LOGGER = LoggerFactory.getLogger(getClass());
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private UserRoleLinkMapper userRoleLinkMapper;
@Autowired
private ResourceService resourceService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LOGGER.info("loadUserByUsername --> [{}]", username);
SysUser sysUser = new SysUser();
sysUser.setUsername(username);
List<SysUser> userList = sysUserMapper.select(sysUser);
if (userList == null || userList.size() == 0) {
throw new UsernameNotFoundException("username not found.");
}
sysUser = userList.get(0);
UserRoleLink userRoleLink = new UserRoleLink();
userRoleLink.setUserId(sysUser.getId());
List<UserRoleLink> userRoleLinks = userRoleLinkMapper.select(userRoleLink);
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
List<Long> roleIds = new ArrayList<>();
Optional<List<UserRoleLink>> userRoleLinksOptional = Optional.ofNullable(userRoleLinks);
userRoleLinksOptional.ifPresent(userRoleLinks1 -> {
userRoleLinks1.forEach(userRoleLink1 -> {
roleIds.add(userRoleLink1.getRoleId());
if(userRoleLink1.getRoleId() == 1L){
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_root");//root角色特权
grantedAuthorities.add(grantedAuthority);
}
});
});
// List<Role> roles = roleMapper.selectBatch(roleIds);//V1.0
List<Resource> resources = resourceService.selectByRoleId(roleIds.toArray(new Long[0]));
if(resources != null && resources.size() > 0){
resources.forEach(resource -> {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+resource.getLink());//必须ROLE_为前缀
grantedAuthorities.add(grantedAuthority);
});
}
LOGGER.info("grantedAuthorities --> {}", grantedAuthorities);
return new User(username, sysUser.getPassword(), true, true, true, true, grantedAuthorities);
}
}
```
### MyAuthenticationProvider
```java
/**
* 自定义验证方式
*/
@Component
public class MyAuthenticationProvider implements AuthenticationProvider{
protected final Logger LOGGER = LoggerFactory.getLogger(getClass());
@Autowired
private MyUserDetailsService userService;
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String username = authentication.getName();
String password = (String) authentication.getCredentials();
UserDetails user = userService.loadUserByUsername(username);
LOGGER.info("password={}, needPassword={}", password, user.getPassword());
//密码匹配验证
if (passwordEncoder().matches(password, user.getPassword())) {
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(user, password, authorities);
}
throw new BadCredentialsException("Wrong password.");
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
```
### MyAccessDecisionManager
```java
/**
* 验证资源跟角色之间的关系
*/
@Component
public class MyAccessDecisionManager implements AccessDecisionManager {
protected final Logger LOGGER = LoggerFactory.getLogger(getClass());
@Autowired
private RoleService roleService;
/**
* // authentication 为用户所被赋予的权限, configAttributes 为访问相应的资源应该具有的权限。
* @param authentication
* @param object
* @param configAttributes
* @throws AccessDeniedException
* @throws InsufficientAuthenticationException
*/
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
Collection<GrantedAuthority> userHasRoles =
(Collection<GrantedAuthority>) authentication.getAuthorities();
LOGGER.info("CurrentUser={} CurrentHasRoles = {}", authentication.getName(), Arrays.asList(userHasRoles));
//放行[超级管理员]角色
Iterator<GrantedAuthority> iterator = userHasRoles.iterator();
while (iterator.hasNext()){
GrantedAuthority grantedAuthority = iterator.next();
if("系统管理员".equals(grantedAuthority.getAuthority())){
return;
}
}
LOGGER.info("1 CurrentUser={} CurrentHasRoles = {}", authentication.getName(), Arrays.asList(userHasRoles));
Collection<GrantedAuthority> uriHasRoles = getGrantedAuthoritys(object);
if (uriHasRoles == null || uriHasRoles.size() == 0) {
return;
}
Optional<Collection<GrantedAuthority>> grantedAuthoritiesForOptional =
Optional.ofNullable(userHasRoles);
try{
grantedAuthoritiesForOptional.ifPresent(userHasRolesNotNull -> {
userHasRolesNotNull.forEach(userHasRole -> {
uriHasRoles.forEach(uriHasRole -> {
LOGGER.info("userHasRole={}, uriHasRole={}", userHasRole, uriHasRole);
if (userHasRole.getAuthority().equals(uriHasRole.getAuthority())) {
throw new AppException("break");
}
});
});
});
}catch(AppException be){
return;
}
throw new AccessDeniedException("Access Denied.");
}
private Collection<GrantedAuthority> getGrantedAuthoritys(Object object) {
FilterInvocation filterInvocation = (FilterInvocation) object;
String uri = new StringBuilder(filterInvocation.getRequestUrl()).deleteCharAt(0).toString();
if("".equals(uri)){
return null;
}
List<Role> uriHasRoles = roleService.selectByResourceURI(uri);
LOGGER.info("fullRequestUrl={}, requestUrl={}, uriHasRoles={}",
filterInvocation.getFullRequestUrl(),
filterInvocation.getRequestUrl(),
uriHasRoles);
if (uriHasRoles == null || uriHasRoles.size() == 0) {
return null;
}
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
uriHasRoles.forEach(item -> {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(item.getName());
grantedAuthorities.add(grantedAuthority);
});
return grantedAuthorities;
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
```
### Remember-Me 使用database方式,MyPersistentTokenRepository
```java
/**
* 记住我 持久化方式
*/
@Component
public class MyPersistentTokenRepository implements PersistentTokenRepository {
protected final Logger LOGGER = LoggerFactory.getLogger(getClass());
@Autowired
private RememberMeMapper remembermeMapper;
@Override
public void createNewToken(PersistentRememberMeToken persistentRememberMeToken) {
RememberMe rememberMe = new RememberMe();
rememberMe.setUsername(persistentRememberMeToken.getUsername());
rememberMe.setSeries(persistentRememberMeToken.getSeries());
rememberMe.setDate(persistentRememberMeToken.getDate());
rememberMe.setTokenValue(persistentRememberMeToken.getTokenValue());
remembermeMapper.insert(rememberMe);
}
@Override
public void updateToken(String s, String s1, Date date) {
RememberMe rememberMe = new RememberMe();
rememberMe.setUsername("");
rememberMe.setSeries(s);
rememberMe.setTokenValue(s1);
rememberMe.setDate(date);
remembermeMapper.updateByPK(rememberMe);
}
@Override
public PersistentRememberMeToken getTokenForSeries(String s) {
RememberMe rememberMe = remembermeMapper.selectByPK(s);
PersistentRememberMeToken persistentRememberMeToken =
new PersistentRememberMeToken(rememberMe.getUsername(),
rememberMe.getSeries(),
rememberMe.getTokenValue(),
rememberMe.getDate()
);
return persistentRememberMeToken;
}
@Override
public void removeUserTokens(String s) {
remembermeMapper.deleteByPK(s);
}
}
```
### 如果用注解的方式,及@RolesAllowed(jsr250)
那么务必将jsr250Enabled=true在config中声明,然后再所需控制的方法上加上注解,
jsr250有三个注解,分别是@RolesAllowed,@PermitAll,@DenyAll,功能跟名字一样,
一目了然,如果有多个角色,可以@RolesAllowed({"ROLE_A", "ROLE_B"}),这里角色的前缀
一定是"ROLE_",约定好的,就不要计较了。
### 结束
使用过程中,总是下意识的按照Spring Security提供的思路去实现,无可厚非,但是我仔细琢磨后,
觉得从便利性和实用方面考虑,每个方法上的注解要约定好名称,例如:
```
@RolesAllowed({"ROLE_roles:view"})
public void find{...}
@RolesAllowed({"ROLE_roles:update"})
public void update{...}
@RolesAllowed({"ROLE_roles:delete"})
public void delete{...}
@RolesAllowed({"ROLE_roles:create"})
public void create{...}
````
这样,表面是把权限赋予了角色,还能Dynamic Resource,何乐而不为呢。
转载于:https://my.oschina.net/nealma/blog/852586
Spring-Boot (四) 集成Spring Security相关推荐
- 【Spring Boot组件集成实战】集成Kaptcha谷歌验证码
更多精彩内容,请访问 Spring Boot组件集成实战专栏 ! 推荐项目:一套基于Spring Boot+Layui的内容管理系统/快速开发脚手架(含完整的开发文档.演示网址等) 文章目录 1. 验 ...
- Spring Boot + Shiro 集成
2019独角兽企业重金招聘Python工程师标准>>> Spring Boot + Shiro 集成 Shiro 是一个流行的 Java 安全框架. 其实 Spring 有一个 Sp ...
- spring boot(四):thymeleaf使用详解
spring boot(四):thymeleaf使用详解 在上篇文章springboot(二):web综合开发中简单介绍了一下thymeleaf,这篇文章将更加全面详细的介绍thymeleaf的使用. ...
- Spring Boot 工程集成全局唯一ID生成器 Vesta
2019独角兽企业重金招聘Python工程师标准>>> 本文内容脑图如下: 文章共 760字,阅读大约需要 2分钟 ! 概 述 在前一篇文章 <Spring Boot工程集成全 ...
- Spring Boot日志集成
Spring Boot日志框架 Spring Boot支持Java Util Logging,Log4j2,Lockback作为日志框架,如果你使用starters启动器,Spring Boot将使用 ...
- 在spring boot中集成Swagger
Swagger 在spring boot中集成Swagger 新建一个swagger项目 maven依赖 <!-- https://mvnrepository.com/artifact/io.s ...
- Spring Boot 快速集成第三方登录功能
Spring Boot 快速集成第三方登录功能 前言 此 demo 主要演示 Spring Boot 项目如何使用 史上最全的第三方登录工具 - JustAuth 实现第三方登录,包括 QQ 登录.G ...
- Spring/Spring boot正确集成Quartz及解决@Autowired失效问题
Spring/Spring boot正确集成Quartz及解决@Autowired失效问题 参考文章: (1)Spring/Spring boot正确集成Quartz及解决@Autowired失效问题 ...
- Spring Boot实践 | 利用Spring Security快速搞定权限控制
目录 开始之前 快速开始 使用内存签名服务 使用数据库签名服务 使用自定义签名服务 限制请求 强制使用HTTPS 防止跨站点伪造请求 用户认证功能 在java web工程中,一般使用Servlet过滤 ...
- Spring Boot 项目使用Spring Security防护CSRF攻击实战
Spring Boot与Spring Security Spring Boot项目结合Spring Security ,可以实现用户认证和用户授权. Spring Security的用户认证可以是配置 ...
最新文章
- ggplot2笔记5:通过图层构建图像
- tensorflow量化感知训练_tensorflow
- 三级结构_kegg pathway三级层级结构转对应表格
- oracle 加载数据戽_走进大数据丨 ETL - Load(数据加载)
- 只能在栈上或者堆上创建对象
- 【Swin Transformer Block】的整体流程如下:
- android 购票代码,android完美电影购票源码
- python爬虫框架scrapy操作步骤
- java实验报告13答案_(完整版)Java程序设计实验报告
- 语音识别技术:2018年主要参与者的完整指南
- 你有被银行套路过吗?| 一文教你计算真实的年化利率
- 二本计算机软件工程专业大学排名,哪些二本大学的软件工程专业最好
- css竖向箭头符号_「右箭头符号」html实现右箭头 - seo实验室
- Linux系统下操作的常用快捷键
- 将HEX文件通过Keil软件进行下载
- 个人用户永久免费,可自动升级版Excel插件,使用VSTO开发,Excel催化剂功能第12波-快速生成、读取、导出条形码二维码...
- 设计模式 - 模版模式
- 【LeetCode】1905-统计子岛屿
- python第七十九天--第十四周作业
- 平狄克微观经济学笔记和课后习题答案
热门文章
- 表示探索、探究的几个词
- Firefox下强制页面缓存失效的设置方法
- NCRE四级网络工程师考题详解----三级索引结构
- 蓝桥杯第五届省赛JAVA真题----n级台阶
- php session作用,PHP中Session的作用
- css 实现一个尖角_一个讲述了 CSS 相关的技巧、动画实现 的开源项目(60篇相关文章)...
- 关闭linux系统中读写页缓存,Linux文件系统FAQ
- concat合并的数组会有顺序么_javascript concat 数组与数组或数组与字符串的合并
- nginx动静分离配置_Nginx动静分离
- java中日历类:Calendar