Shiro安全框架(使用总结)

标签: shiro框架  shiro

07Shiro笔记


Shiro 安全框架

1.简介

  • Shiro是一个安全框架,用于解决系统的认证和授权问题,同时提供了会话管理,数据加密,WEB集成,缓存等机制。

  • 身份验证: 即验证用户是不是拥有相应的身份。

    在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,来验证用户的身份:

    principals:身份,即主体的标识属性,可以是用户名、邮箱、手机号等,唯一即可。

    credentials:证明 / 凭证,即只有主体知道的安全值,如密码 / 数字证书等。

    最常见的 principals 和 credentials 组合就是用户名 / 密码。

  • 授权,也叫访问控制,即在应用中控制谁能访问哪些资源。

2.传统认证方式与Shior认证方式的对比

传统认证方式:

img

shiro认证方式:

img

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();
    }

从如上代码可总结出身份验证的步骤

  1. 收集用户身份 / 凭证,即如用户名 / 密码;
  2. 调用 Subject.login 进行登录,如果失败将得到相应的 AuthenticationException 异常,根据异常提示用户错误信息;否则登录成功;
  3. 最后调用 Subject.logout 进行退出操作。

5.身份认证流程

img

流程如下:

  1. 首先调用 Subject.login(token) 进行登录,其会自动委托给 Security Manager,调用之前必须通过 SecurityUtils.setSecurityManager() 设置;
  2. SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
  3. Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
  4. Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
  5. 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(增查改删)式权限控制)打印文档等

授权流程

img

流程如下:

  1. 首先调用 Subject.isPermitted*/hasRole*接口,其会委托给 SecurityManager,而 SecurityManager 接着会委托给 Authorizer;
  2. Authorizer 是真正的授权者,如果我们调用如 isPermitted(“user:view”),其首先会通过 PermissionResolver 把字符串转换成相应的 Permission 实例;
  3. 在进行授权之前,其会调用相应的 Realm 获取 Subject 相应的角色/权限用于匹配传入的角色/权限;
  4. 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"
版权声明:本文为nglhaeg原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/nglhaeg/article/details/108322824