public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
HttpServletRequest httpRequest=(HttpServletRequest)request;
HttpServletResponse httpResponse=(HttpServletResponse)response;
HttpSession session=httpRequest.getSession();
if(session.getAttribute("username")!=null){
chain.doFilter(request, response); //如果可以放行
} else {
httpResponse.sendRedirect(httpRequest.getContextPath()+"/login.jsp");
}
}
ACL: Access Control List 访问控制列表
RBAC: Role Based Access Control
BAT企业 ACL,一般是对报表系统,阿里的ODPS
总结:不能过于复杂,规则过多,维护性和性能会下降, 更多分类 ABAC、PBAC等
什么是 spring Security:官网基础介绍
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
一句话:Spring Security 的前身是 Acegi Security ,是 Spring 项目组中用来提供安全认证服务的框架
什么是 Apache Shiro:官网基础介绍
https://github.com/apache/shiro
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
一句话:Shiro是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能
两个优缺点,应该怎么选择
Apache Shiro比Spring Security , 前者使用更简单
Shiro 功能强大、 简单、灵活, 不跟任何的框架或者容器绑定,可以独立运行
Spring Security 对Spring 体系支持比较好,脱离Spring体系则很难开发
SpringSecutiry 支持Oauth鉴权 https://spring.io/projects/spring-security-oauth,Shiro需要自己实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IzkGDURV-1626417693497)(http://shiro.apache.org/assets/images/ShiroFeatures.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aTmV5FDL-1626417693504)(http://shiro.apache.org/assets/images/ShiroArchitecture.png)]
更多资料导航:http://shiro.apache.org/reference.html
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>com.zaxxergroupId>
<artifactId>HikariCP-java7artifactId>
<version>2.4.13version>
dependency>
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.3.2version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.4.1version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
权限标准五张表:
/**
* 创建 realm, realm中有两个方法。
*/
public class CustomizeRealm extends AuthorizingRealm {
// 认证, 只要成功返回 AuthenticationInfo,
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("开始认证");
if(authenticationToken instanceof UsernamePasswordToken) {
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
String username = token.getUsername();
String password = new String(token.getPassword()).intern();
// 到db验证
System.out.println("username: " + username + "## password: " + password);
AuthenticationInfo info = new SimpleAuthenticationInfo(username, password, "XX");
return info;
}
return null;
}
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权");
return null;
}
}
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 设置 SecurityManager, 负责整个shiro的运作
factoryBean.setSecurityManager(securityManager);
factoryBean.setLoginUrl("/login.html");
Map<String, String> filterMap = new HashMap<>();
/**
* map的key是访问的路径,map的value是固定值:anno, authc
* anon -> anonymous(匿名)
* authc -> authencation(认证)
*/
filterMap.put("/login", "anon"); // 认证通过之后才能访问
// filterMap.put("/user", "authc"); // 认证通过之后才能访问
// filterMap.put("/test", "authc"); // 可以匿名访问,说白了就不需要登录
filterMap.put("/**", "authc");
factoryBean.setFilterChainDefinitionMap(filterMap);
return factoryBean;
}
/**
* SecurityManager的配置
*/
@Bean
public SecurityManager securityManager(SessionManager manager, Realm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setSessionManager(sessionManager);
securityManager.setRealm(realm);
return securityManager;
}
/**
* 该类的作用是,实现注解的方式来设置权限
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator advisor = new DefaultAdvisorAutoProxyCreator();
advisor.setProxyTargetClass(true);
return advisor;
}
/**
* 实现注解的方式来配置权限
*/
@Bean
public AuthorizationAttributeSourceAdvisor
authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new
AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
*/
/**
* SessionManager的配置
*/
@Bean
public SessionManager sessionManager(){
SessionManager sessionManager = new CustomSessionManager();
return sessionManager;
}
/**
* Realm
* @param addSaltCredentialsMatch
* @return
*/
@Bean
public Realm realm(AddSaltCredentialsMatch addSaltCredentialsMatch) {
CustomRealm realm = new CustomRealm();
realm.setCredentialsMatcher(addSaltCredentialsMatch); //密码规则
return realm;
}
/**
* 自定义的密码加盐规则
* @return
*/
@Bean
public AddSaltCredentialsMatch addSaltCredentialsMatch() {
return new AddSaltCredentialsMatch();
}
}
@RequestMapping("/login")
@RestController
public class LoginController {
@GetMapping
public String login(String username, String password) {
// 获取一个主题(用户)
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
subject.login(token);
return "success";
}
}
public class CustomSessionManager extends DefaultWebSessionManager {
/**
* 重写默认的session
* @param request
* @param response
* @return
*/
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
String sessionId = WebUtils.toHttp(request).getHeader("Authencation");
if(null != sessionId) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, sessionId);
//automatically mark it valid here. If it is invalid, the
//onUnknownSession method below will be invoked and we'll remove the attribute at that time.
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return sessionId;
}else {
return super.getSessionId(request, response);
}
}
}
redis作为企业使用最为频繁的中间件,用来缓存各种业务数据,在使用shiro的缓存的时候,课程中还是采用redis来作为缓存中间件。下载地址:https://github.com/MicrosoftArchive/redis/releases
<dependency>
<groupId>org.crazycakegroupId>
<artifactId>shiro-redisartifactId>
<version>3.2.3version>
dependency>
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost("localhost:6379");
return redisManager;
}
@Bean
public CacheManager cacheManager(RedisManager redisManager) {
RedisCacheManager cacheManager = new RedisCacheManager();
cacheManager.setRedisManager(redisManager);
return cacheManager;
}
securityManager.setCacheManager(cacheManager);
@Bean
public RedisSessionDAO redisSessionDAO(RedisManager redisManager) {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager);
return redisSessionDAO;
}
@Bean
public SessionManager sessionManager(RedisSessionDAO redisSessionDAO){
CustomSessionManager sessionManager = new CustomSessionManager();
sessionManager.setSessionDAO(redisSessionDAO);
return sessionManager;
}
/**
shiro内部提供了做两次md5处理。但是得到数据与其他工具类,得到两次md5数据不一致。
*/
@Bean
public CredentialsMatcher credentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5"); //设置加密方式
hashedCredentialsMatcher.setHashIterations(2); //作两次md5加密
return hashedCredentialsMatcher;
}
处理方式:在用户注册的时候,存入密码的时候就按照shiro的规则来储存密码。
new SimpleHash("md5", "123", null, 2).toString() // 将这种处理方式得到的密码存入数据库。