Spring Security
1. 简介
Spring Security是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选项 他可以实现强大的Web安全控制,对于安全控制我们仅需要引入spring-boot-starter-security模块,进行少量的配置,即可实现强大的安全管理
2. 几个重要的概念和类
重要的类
WebSecurityConfigurerAdapter:自定义Security策略 AuthenticationManagerBuilder:自定义认证策略 @EnableWebSecurity:开启WebSecurity模式
Spring Security的两个重要目标是"认证(Authentication)“和"授权”(Authorization)
3. 基本配置
pom.xml
< ! -- security -->
< dependency>
< groupId> org.springframework.boot< /groupId>
< artifactId> spring-boot-starter-security< /artifactId>
< /dependency>
SecurityConfig.java
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure ( HttpSecurity http) throws Exception {
规则
http. authorizeRequests ( )
. antMatchers ( "/" ) . permitAll ( )
. antMatchers ( "/toLogin" ) . permitAll ( )
. antMatchers ( "/level1/**" ) . hasRole ( "vip1" )
. antMatchers ( "/level2/**" ) . hasRole ( "vip2" )
. antMatchers ( "/level3/**" ) . hasRole ( "vip3" ) ;
/ / loginPage: 定制登录页面,即用自己写的登录界面,当请求/ toLogin进入Security登录
/ / loginProcessingUrl:登陆成功放行的请求,即前端的url= / loginSuccess
/ / / toLogin对应Controller定义的一个请求,/ loginSuccess可理解为Security内定的一个方法的路径
http. formLogin ( ) . loginPage ( "/toLogin" ) . loginProcessingUrl ( "/loginSuccess" ) ;
/ / 注销;开启注销功能,默认/ logout ( 自动配置的)
/ / http. logout ( ) ;
http. logout ( ) . logoutSuccessUrl ( "/" ) ;
/ / 防止网站工具:get,post
http. csrf ( ) . disable ( ) ;
/ / 开启记录功能 cookie 默认保存两周
/ / http. rememberMe ( ) ;
http. rememberMe ( ) . rememberMeParameter ( "remember" ) ;
}
/ / 认证, SpringBoot 2.1 . x 可以直接使用
/ / 密码编码: PasswordEncoder
@Override
protected void configure ( AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder ( ) ;
/ / 这些数据正常应该从数据库中读
/ / 从内存获取
auth. inMemoryAuthentication ( ) . passwordEncoder ( encoder)
. withUser ( "admin" ) . password ( encoder. encode ( "1" ) ) . roles ( "vip1" , "vip2" )
. and ( )
. withUser ( "root" ) . password ( encoder. encode ( "123" ) ) . roles ( "vip1" , "vip2" , "vip3" ) ;
}
}
4. 整合thymeleaf的一些功能
pom.xml
< dependency>
< groupId> org.thymeleaf.extras< /groupId>
< artifactId> thymeleaf-extras-java8time< /artifactId>
< /dependency>
index.html
< html lang= "en" xmlns:th= "http://www.thymeleaf.org"
xmlns:sec= "http://www.thymeleaf.org/thymeleaf-extras-springsecurity4" >
< ! -- 如果未登录,判断,条件成立该标签才生效 -->
< div sec:authorize= "!isAuthenticated()" >
< ! --未登录-->
< a class= "item" th:href= "@{/toLogin}" >
< i class= "address card icon" > < /i> 登录
< /a>
< /div>
< ! -- 如果登录 :显示用户名,判断,条件成立该标签才生效-->
< div sec:authorize= "isAuthenticated()" >
< ! -- 获取用户名 -->
< a class= "item" >
用户名:< span sec:authentication= "name" > < /span>
< /a>
< /div>
< ! -- 如果登录 :显示注销-->
< div sec:authorize= "isAuthenticated()" >
< ! --注销-->
< a class= "item" th:href= "@{/logout}" >
< i class= "sign out icon" > < /i> 注销
< /a>
< /div>
Shiro
基本配置
pom.xml
< dependency>
< groupId> org.apache.shiro< /groupId>
< artifactId> shiro-core< /artifactId>
< version> 1.4.1< /version>
< /dependency>
< ! -- configure logging -->
< dependency>
< groupId> org.slf4j< /groupId>
< artifactId> jcl-over-slf4j< /artifactId>
< version> 1.7.21< /version>
< scope> runtime< /scope>
< /dependency>
< dependency>
< groupId> org.slf4j< /groupId>
< artifactId> slf4j-log4j12< /artifactId>
< version> 1.7.21< /version>
< scope> runtime< /scope>
< /dependency>
< dependency>
< groupId> log4j< /groupId>
< artifactId> log4j< /artifactId>
< version> 1.2.17< /version>
< scope> runtime< /scope>
< /dependency>
日志配置
log4j.rootLogger= INFO, stdout
log4j.appender.stdout= org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout= org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern= %d %p [ %c] - %m %n
log4j.logger.org.apache= WARN
log4j.logger.org.springframework= WARN
log4j.logger.org.apache.shiro= INFO
log4j.logger.org.apache.shiro.util.ThreadContext= WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache= WARN
shiro.ini
[ users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
[ roles]
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5
基本用法
Subject currentUser = SecurityUtils. getSubject ( ) ;
Session session = currentUser. getSession ( ) ;
session. setAttribute ( "someKey" , "aValue" ) ;
currentUser. isAuthenticated ( )
currentUser. hasRole ( "schwartz" )
currentUser. isPermitted ( "lightsaber:wield" )
currentUser. logout ( ) ;
Springboot + Shiro
1. 基本配置
pom.xml
< ! -- shiro整合boot -->
< dependency>
< groupId> org.apache.shiro< /groupId>
< artifactId> shiro-spring< /artifactId>
< version> 1.4.1< /version>
< /dependency>
config包下ShiroConfig
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean ( @Qualifier ( "securityManager" ) DefaultWebSecurityManager manager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean ( ) ;
bean. setSecurityManager ( manager) ;
HashMap< String, String> map = new HashMap < > ( ) ;
map. put ( "/user/*" , "authc" ) ;
bean. setFilterChainDefinitionMap ( map) ;
bean. setLoginUrl ( "/toLogin" ) ;
return bean;
}
/ / DefaultWebSecurityManager
@Bean ( "securityManager" )
public DefaultWebSecurityManager defaultWebSecurityManager ( @Qualifier ( "userRealm" ) UserRealm userRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager ( ) ;
/ / 关联UserRealm
manager. setRealm ( userRealm) ;
return manager;
}
/ / 创建realm对象,需要自定义类
@Bean
public UserRealm userRealm ( ) {
return new UserRealm ( ) ;
}
config包下自定义的UserRealm
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo ( PrincipalCollection principals) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo ( AuthenticationToken token) throws AuthenticationException {
return null;
}
}
2. 基本流程
在ShiroFilterFactoryBean中进行一些路径的配置
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean ( DefaultWebSecurityManager manager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean ( ) ;
bean. setSecurityManager ( manager) ;
HashMap< String, String> map = new HashMap < > ( ) ;
map. put ( "/user/add" , "perms[user:add]" ) ;
map. put ( "/user/update" , "perms[user:update]" ) ;
map. put ( "/user/*" , "authc" ) ;
bean. setFilterChainDefinitionMap ( map) ;
bean. setLoginUrl ( "/toLogin" ) ;
/ / 未授权页面
bean. setUnauthorizedUrl ( "/noauth" ) ;
return bean;
}
在自定义的Realm中设置授权和认证相关的东西
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo ( PrincipalCollection principals) {
System. out. println ( "执行了===>授权" ) ;
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo ( ) ;
Subject subject = SecurityUtils. getSubject ( ) ;
User user = ( User) subject. getPrincipal ( ) ;
info. addStringPermission ( user. getPerms ( ) ) ;
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo ( AuthenticationToken token) throws AuthenticationException {
System. out. println ( "执行了===>认证" ) ;
UsernamePasswordToken userToken = ( UsernamePasswordToken) token;
User user = userService. queryUserByName ( userToken. getUsername ( ) ) ;
if ( user == null) {
return null;
}
return new SimpleAuthenticationInfo ( user, user. getPwd ( ) , "" ) ;
}
}
Controller的一些相关操作
@RequestMapping ( "/login" )
public String login ( String username, String password, Model model) {
Subject subject = SecurityUtils. getSubject ( ) ;
UsernamePasswordToken token = new UsernamePasswordToken ( username, password) ;
try {
subject. login ( token) ;
subject. getSession ( ) . setAttribute ( "HasUser" , true ) ;
return "/index" ;
} catch ( UnknownAccountException e) {
model. addAttribute ( "msg" , "用户名错误" ) ;
return "login" ;
} catch ( IncorrectCredentialsException e) {
model. addAttribute ( "msg" , "密码错误" ) ;
return "login" ;
}
}
@RequestMapping ( "/logout" )
public String logout ( ) {
Subject subject = SecurityUtils. getSubject ( ) ;
subject. getSession ( ) . removeAttribute ( "HasUser" ) ;
subject. logout ( ) ;
return "index" ;
}
3. 导入shiro整合thymeleaf的前端
pom.xml
< ! -- shiro整合thymeleaf -->
< dependency>
< groupId> com.github.theborakompanioni< /groupId>
< artifactId> thymeleaf-extras-shiro< /artifactId>
< version> 2.0.0< /version>
< /dependency>
< dependency>
< groupId> org.thymeleaf< /groupId>
< artifactId> thymeleaf-spring5< /artifactId>
< /dependency>
< dependency>
< groupId> org.thymeleaf.extras< /groupId>
< artifactId> thymeleaf-extras-java8time< /artifactId>
< /dependency>
需要加入ShiroDialect bean
@Bean
public ShiroDialect getShiroDialect( ) {
return new ShiroDialect( ) ;
}
index.xml
< h1> 首页< /h1>
< a th:if= "${session.get('HasUser')== null} " th:href= "@{/toLogin}" > 登录< /a>
< a th:if= "${session.get('HasUser')== true} " th:href= "@{/logout}" > 注销< /a>
< p th:text= "${msg} " > < /p>
< ! -- 是否有权限 -->
< a shiro:hasPermission= "user:add" th:href= "@{/user/add}" > add< /a>
< a shiro:hasPermission= "user:update" th:href= "@{/user/update}" > update< /a>