07Shiro笔记
Shiro 安全框架
1.简介
-
Shiro是一个安全框架,用于解决系统的认证和授权问题,同时提供了会话管理,数据加密,WEB集成,缓存等机制。
-
身份验证: 即验证用户是不是拥有相应的身份。
在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,来验证用户的身份:
principals:身份,即主体的标识属性,可以是用户名、邮箱、手机号等,唯一即可。
credentials:证明 / 凭证,即只有主体知道的安全值,如密码 / 数字证书等。
最常见的 principals 和 credentials 组合就是用户名 / 密码。
-
授权,也叫访问控制,即在应用中控制谁能访问哪些资源。
2.传统认证方式与Shior认证方式的对比
传统认证方式:
shiro认证方式:
3.Shiro认证流程及架构图
Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”;
SecurityManager:相当于 SpringMVC 中的 DispatcherServlet ;是 Shiro 的核心;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authorizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;
SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所有呢,Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);
SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密 / 解密的。
4.Shiro身份认证的实现
-
导入依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.5.3</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.2</version> </dependency>
-
准备用户的身份 / 凭据
SimpleAccountRealm accountRealm = new SimpleAccountRealm();
@BeforeEach
public void before(){
accountRealm.addAccount("zhangsan","123");
}
- 测试用例
@Test
void testAuthentication() {
//创建安全环境
DefaultSecurityManager manager = new DefaultSecurityManager();
//注入域对象
manager.setRealm(accountRealm);
SecurityUtils.setSecurityManager(manager);
//得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "123");
try {
//登录,即身份验证
subject.login(token);
} catch (AuthenticationException e) {
//身份验证失败
}
assertEquals(true,subject.isAuthenticated()); //断言用户已经登录
//退出
subject.logout();
}
从如上代码可总结出身份验证的步骤:
- 收集用户身份 / 凭证,即如用户名 / 密码;
- 调用 Subject.login 进行登录,如果失败将得到相应的 AuthenticationException 异常,根据异常提示用户错误信息;否则登录成功;
- 最后调用 Subject.logout 进行退出操作。
5.身份认证流程
流程如下:
- 首先调用 Subject.login(token) 进行登录,其会自动委托给 Security Manager,调用之前必须通过 SecurityUtils.setSecurityManager() 设置;
- SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
- Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
- Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
- Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。
6.Shiro提供的内置Realm
Realm
Realm:域,Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。
- org.apache.shiro.realm.text.IniRealm:[users] 部分指定用户名 / 密码及其角色;[roles] 部分指定角色即权限信息;
[users]
zhang=123
wang=123
此处使用 ini 配置文件,通过 [users] 指定了两个主体:zhang/123、wang/123
- 测试用例
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
@Test
void testAuthentication() {
DefaultSecurityManager manager = new DefaultSecurityManager();
manager.setRealm(iniRealm);
SecurityUtils.setSecurityManager(manager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
}
assertEquals(true,subject.isAuthenticated());
subject.logout();
}
-
org.apache.shiro.realm.jdbc.JdbcRealm:通过 sql 查询相应的信息
JDBC Realm 使用
1、数据库及依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
2、到数据库 shiro 下建三张表:users(username/password)、user_roles(role_name/ username)、roles_permissions(permission /role_name)
3、测试用例
JdbcRealm jdbcRealm = new JdbcRealm();
@BeforeEach
public void before(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUsername("root");
druidDataSource.setPassword("root");
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
jdbcRealm.setDataSource(druidDataSource);
jdbcRealm.setPermissionsLookupEnabled(true);
}
@Test
public void testAuthentication(){
DefaultSecurityManager manager = new DefaultSecurityManager(jdbcRealm);
SecurityUtils.setSecurityManager(manager);
Subject subject = SecurityUtils.getSubject();
String username;
String password;
UsernamePasswordToken token = new UsernamePasswordToken("zs","123");
subject.login(token);
assertEquals(true,subject.isAuthenticated());
subject.checkRole("normal");
subject.checkPermissions("user:list","user:delete");
subject.logout();
}
7.与SpringMvcWeb集成实现认证
Shiro 提供了与 Web 集成的支持,其通过一个 ShiroFilter 入口来拦截需要安全控制的 URL,然后进行相应的控制,ShiroFilter 类似于如 SpringMVC 这种 web 框架的前端控制器,其是安全控制的入口点,判断 URL 是否需要登录 / 权限等工作。
- 环境准备
- 导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.3</version>
</dependency>
- web.xml配置
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 自定义域实现认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String)token.getPrincipal();
User user = userService.findByUsername(username);
if(user == null) {
//没找到帐号
throw new UnknownAccountException();
}
if(user.getStatus().equals(SysConstants.LOCKED_STATUS)) {
//帐号锁定
throw new LockedAccountException();
}
//交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user.getUsername(), //用户名
user.getPassword(), //密码
ByteSource.Util.bytes(user.getSalt()),
getName() //realm name
);
return authenticationInfo;
}
- springmvc配置Shiro
<!--controller包扫描-->
<context:component-scan base-package="com.woniuxy.sys.controller">
</context:component-scan>
<!--注解驱动-->
<mvc:annotation-driven />
<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>
<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--自定义域-->
<bean id="realm" class="com.woniuxy.realm.CustomRealm"></bean>
<!--安全管理器-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="realm"></property>
</bean>
<!--过滤工厂bean-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<property name="unauthorizedUrl" value="403.html"></property>
<property name="loginUrl" value="login.html"></property>
<property name="filterChainDefinitions">
<value>
/login.html=anon
/doLogin=anon
/*=authc
</value>
</property>
</bean>
- Controller中进行提交认证
@PostMapping("/doLogin")
public String doLogin(User user){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
return "fail";
}
return "success";
}
8.实现授权
授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。
主体
主体,即访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。
资源
在应用中用户可以访问的URL,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。
权限
安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如: 访问用户列表页面
查看/新增/修改/删除用户数据(即很多时候都是 CRUD(增查改删)式权限控制)打印文档等
授权流程
流程如下:
- 首先调用
Subject.isPermitted*/hasRole*
接口,其会委托给 SecurityManager,而 SecurityManager 接着会委托给 Authorizer; - Authorizer 是真正的授权者,如果我们调用如 isPermitted(“user:view”),其首先会通过 PermissionResolver 把字符串转换成相应的 Permission 实例;
- 在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限用于匹配传入的角色/权限;
- Authorizer 会判断 Realm 的角色/权限是否和传入的匹配,如果有多个 Realm,会委托给 ModularRealmAuthorizer 进行循环判断,如果匹配如会返回 true,否则返回 false 表示授权失败。
- 自定义域实现授权
public class CustomRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
String username = principalCollection.getPrimaryPrincipal().toString();
Set role = new HashSet();
if(username.equals("hello")){
role.add("admin");
}
Set perms = new HashSet();
if(username.equals("hello")){
perms.add("user:list");
perms.add("user:update");
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(role);
info.setStringPermissions(perms);
return info;
}
//登录时执行认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = authenticationToken.getPrincipal().toString();
User user = null;
if(username.equals("hello")){
user = new User();
user.setUsername(username);
user.setPassword("2c28917916001d69078a15b8ab3e5d77");//123
user.setStatus(1);
}
if(user==null){
throw new UnknownAccountException("账户不存在");
}
if(user != null && user.getStatus()==0){
throw new LockedAccountException("账号已经被锁定");
}
//返回认证信息
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),
ByteSource.Util.bytes("hello"),this.getName());
return info;
}
//测试用于获取加密后的密码
// public static void main(String[] args) {
// Md5Hash md5Hash = new Md5Hash("123","hello",10);
// System.out.println(md5Hash);
// }
}
9.Shiro内置过滤器
内置过滤器 | 过滤器类 | 备注 |
---|---|---|
anon | org.apache.shiro.web.filter.authc.AnonymousFilter | 无参,表示可匿名访问 |
authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter | 无参,表示需要认证才能访问 |
authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter | 无参,表示需要httpBasic认证才能访问 |
perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter | 表示用户必需已通过认证,并拥有权限才可以正常发起请求 |
port | org.apache.shiro.web.filter.authz.PortFilter | 指定URL端口 |
rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter | 根据请求方式来识别,相当于 /admins/user/**=perms[user:get]或perms[user:post] 等等 |
roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter | 表示用户必需已通过认证,并拥有角色才可以正常发起请求 |
ssl | org.apache.shiro.web.filter.authz.SslFilter | 无参,表示需要安全的URL请求,协议为https |
user | org.apache.shiro.web.filter.authc.UserFilter | 表示用户不一定需要通过认证,只要曾被 Shiro 记住过登录状态就可以正常发起 /home 请求 |
logout | org.apache.shiro.web.filter.authc.LogoutFilter | 登出 |
10.Shiro标签
*标签名称* | *标签条件(均是显示标签内容)* |
---|---|
shiro:authenticated | 已认证通过的用户。不包含已记住的用户,这是与user标签的区别。 |
shiro:notAuthenticated | 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户 |
shiro:guest | 验证当前用户是否为“访客”,即未认证的用户 |
shiro:user | 认证通过或已记住的用户。 |
shiro:hasAnyRoles | 验证当前用户是否属于任意一个角色。sh |
shiro:hsaRole | 验证当前用户是否属于该角色。 |
shiro:lacksRole | 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。 |
shiro:hasPermission | 验证当前用户是否拥有指定权限 |
shiro:lacksPermission | 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。 |
shiro:principal | 输出当前用户信息,通常为登录帐号信息。 |
11.Shiro权限注解
Shiro提供了相应的注解用于权限控制,如果使用这些注解就需要使用AOP的功能来进行判断,Shiro提供了Spring AOP集成用于权限注解的解析和验证。
- 配置依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
- 在spring-mvc.xml配置文件添加Shiro Spring AOP权限注解的支持:
<aop:config proxy-target-class="true"></aop:config>
<bean class="
org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
-
Shiro权限注解
@RequiresAuthentication 表示当前Subject已经通过login进行了身份验证;即Subject. isAuthenticated()返回true。
@RequiresRoles 表示当前Subject需要某些角色
@RequiresPermissions表示当前Subject需要某些权限
-
当验证失败,其会抛出UnauthorizedException异常,此时可以使用Spring的ExceptionHandler(DefaultExceptionHandler)来进行拦截处理:
@ExceptionHandler({UnauthorizedException.class})
public ModelAndView processUnauthenticatedException() {
ModelAndView mv = new ModelAndView();
mv.setViewName("forward:/403.html");
return mv;
}
12.Shiro加密
散列算法
散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如 MD5、SHA 等。一般进行散列时最好提供一个 salt(盐),比如加密密码 “admin”,产生的散列值是 “21232f297a57a5a743894a0e4a801fc3”,可以到一些 md5 解密网站很容易的通过散列值得到密码 “admin”,即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如用户名和 ID(即盐);这样散列的对象是 “密码 + 用户名 +ID”,这样生成的散列值相对来说更难破解。
String str = "hello";
String salt = "123";
String md5 = new Md5Hash(str, salt).toString();//
如上代码通过盐 “123”MD5 散列 “hello”。另外散列时还可以指定散列次数,如 2 次表示:md5(md5(str)):“new Md5Hash(str, salt, 2).toString()”。
HashedCredentialsMatcher 实现密码验证服务
Shiro 提供了 CredentialsMatcher 的散列实现 HashedCredentialsMatcher,用于密码验证。
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(),
user.getPassword(),ByteSource.Util.bytes("盐值"),this.getName());
配置密码匹配器
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" id="credentialsMatcher">
<property name="hashIterations" value="1024"></property>
<property name="hashAlgorithmName" value="md5"></property>
</bean>
<!--注入密码匹配器-->
<bean id="realm" class="com.woniuxy.realm.CustomRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"></property>
</bean>
13.缓存
Shiro只提供了一个可以支持具体缓存实现(如:Hazelcast, Ehcache, OSCache, Terracotta, Coherence, GigaSpaces, JBossCache等)的抽象API接口,这样就允许Shiro用户根据自己的需求灵活地选择具体的CacheManager。当然,其实Shiro也自带了一个本地内存CacheManager:org.apache.shiro.cache.MemoryConstrainedCacheManager。
<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager">
</bean>
14.记住我
配置
<bean class="org.apache.shiro.web.mgt.CookieRememberMeManager" id="rememberMeManager">
<property name="cookie" ref="simpleCookie"></property>
</bean>
<bean class="org.apache.shiro.web.servlet.SimpleCookie" id="simpleCookie">
<constructor-arg value="rememberMe"></constructor-arg>
<property name="maxAge" value="180"></property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="realm"></property>
<property name="rememberMeManager" ref="rememberMeManager"></property>
</bean>
代码实现
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
token.setRememberMe(true);
要实现记住我功能,必须将管理的model对象实现可序列化(implements Serializable)
在athc过滤配置之前,能够通过记住我访问的页面要配置user过滤器
15.与SpringBoot集成
- 配置依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.1</version>
</dependency>
<--注意: 这里不需要添加 spring-boot-starter-web 依赖,因为 shiro-spring-boot-web-starter 中已经依赖了 spring-boot-starter-web-->
-
配置类webmvc
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login"); registry.addViewController("/index").setViewName("index"); } }
-
自定义域(继承AutorizingRealm)
public class CustomRealm extends AuthorizingRealm { //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = principals.getPrimaryPrincipal().toString(); //为了方便,就没有去查数据库,角色和权限应该从数据库中查 //角色 Set role = new HashSet(); //权限 Set perms = new HashSet(); if(username.equals("hello")){ System.out.println("hahahahha"); role.add("admin"); perms.add("user:list"); } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addRoles(role); info.addStringPermissions(perms); return info; } //验证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //获取用户名 String username = authenticationToken.getPrincipal().toString(); User user = null; if(username.equals("hello")){ user = new User(); user.setUsername(username); user.setPassword("2c28917916001d69078a15b8ab3e5d77"); user.setStatus(1); } if(user == null){ throw new UnknownAccountException("账号不存在"); } //创建认证信息对象 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes("hello"),this.getName()); return simpleAuthenticationInfo; } //测试获取加密的密码 // public static void main(String[] args) { // Md5Hash md5Hash = new Md5Hash("123","hello",10); // System.out.println(md5Hash); // } }
-
Shiro配置
//自定义域
//value = "authorizer",有一个系统默认的安全管理器,如果自己不写安全管理器,这个值必须加
@Bean/*(value = "authorizer")*/
public CustomRealm customRealm(CacheManager cacheManager, CredentialsMatcher credentialsMatcher){
CustomRealm realm= new CustomRealm();
realm.setCacheManager(cacheManager);
realm.setCredentialsMatcher(credentialsMatcher);
return realm;
}
//配置安全管理器(可以不配置,系统会自动生成,要在自定义域的注解上加value=)
@Bean
public DefaultWebSecurityManager securityManager(CustomRealm customRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
//设置域对象
manager.setRealm(customRealm);
//设置缓存管理器
manager.setCacheManager(credentialsCache());
//记住我
manager.setRememberMeManager(rememberMeManager());
return manager;
}
//过滤工厂bean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//设置访问未认证页面跳转的路径
shiroFilterFactoryBean.setLoginUrl("login");
//设置访问未授权页面跳转的路径
shiroFilterFactoryBean.setUnauthorizedUrl("403");
//创建权限map
Map map = new LinkedHashMap();
map.put("/login.html","anon");
map.put("/js/**","anon");
map.put("/doLogin","anon");
map.put("/login","anon");
map.put("/index","user");
//表用户已通过认证,但必须有root角色才能访问
// map.put("/ceshi","roles[root]");
//表示用户已通过认证,但必须有这个全选才能访问
map.put("/ceshi","perms[search]");
map.put("/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
// //配置shiro过滤规则
// @Bean
// public ShiroFilterChainDefinition shiroFilterChainDefinition(){
// //创建默认的过滤链对象
// DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
// definition.addPathDefinition("/login.html","anon");
// definition.addPathDefinition("/js/**","anon");
// definition.addPathDefinition("/doLogin","anon");
// definition.addPathDefinition("/login","anon");
// definition.addPathDefinition("/index","user");
// definition.addPathDefinition("/**","authc");
// return definition;
// }
//密码匹配器
@Bean
public HashedCredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashIterations(2);
matcher.setHashAlgorithmName("md5");
return matcher;
}
//对查询的权限,开启缓存,不用每次去查
@Bean
public MemoryConstrainedCacheManager cacheManager(){
MemoryConstrainedCacheManager manager = new MemoryConstrainedCacheManager();
return manager;
}
//这个bean是为了支持在Theymeleaf中使用shiro标签,
// 同时要加依赖thymeleaf-extras-shiro
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
//记住我功能设置cookie
@Bean
public SimpleCookie simpleCookie(){
SimpleCookie simpleCookie =new SimpleCookie("rememberMe");
//记住我cookie生效时间,单位秒
simpleCookie.setMaxAge(180);
return simpleCookie;
}
//设置cookie管理器
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
rememberMeManager.setCookie(simpleCookie());
return rememberMeManager;
}
-
UserController
@Controller
public class UserController {
@ResponseBody
@PostMapping("/doLogin")
public String login(User user,String rememberMe){
//获取主体
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
if(rememberMe!=null && rememberMe.equals("on")){
token.setRememberMe(true);
}
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
return "falid";
}
return "success";
}
@RequestMapping("/delete")
@ResponseBody
@RequiresPermissions("user:delete")
public String delete(){
System.out.println("delete");
return "delete";
}
}
- thymeleaf使用Shiro标签
- 配置依赖
```xml
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
- 注册ShiroDialect
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
return "falid";
}
return "success";
}
@RequestMapping("/delete")
@ResponseBody
@RequiresPermissions("user:delete")
public String delete(){
System.out.println("delete");
return "delete";
}
}
- thymeleaf使用Shiro标签
- 配置依赖
```xml
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
- 注册ShiroDialect
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
- Html导入命名空间 : xmlns:shiro=”http://www.pollix.at/thymeleaf/shiro"