zoukankan      html  css  js  c++  java
  • shiro初体验

    shiro底层就是一个过滤器

    springMvc采用请求转发,相当于只发送一次请求,转发的请求不会被拦截

    redirect 重定向,相当于发送两次请求,重新发送的请求也会被拦截

    springboot中默认放行的static下的文件也会被shiro拦截

    redirect 重定向,相当于发送两次请求,重新发送的请求也会被拦截

    springboot中默认放行的static下的文件也会被shiro拦截

    1. 添加依赖

     1          <dependency>
     2             <groupId>com.alibaba</groupId>
     3             <artifactId>druid-spring-boot-starter</artifactId>
     4             <version>1.1.17</version>
     5         </dependency>
     6         <dependency>
     7             <groupId>com.baomidou</groupId>
     8             <artifactId>mybatis-plus-boot-starter</artifactId>
     9             <version>3.3.1</version>
    10         </dependency>
    11         <dependency>
    12             <groupId>org.apache.shiro</groupId>
    13             <artifactId>shiro-spring</artifactId>
    14             <version>1.5.2</version>
    15         </dependency>
    16         <dependency>
    17             <groupId>com.github.theborakompanioni</groupId>
    18             <artifactId>thymeleaf-extras-shiro</artifactId>
    19             <version>2.0.0</version>
    20         </dependency>               

    2. pojo类

     1 //用户
     2 @Data
     3 @Accessors(chain = true)
     4 public class User {
     5     private Integer uId;
     6     private String name;
     7     private String password;
     8     @TableField(exist = false)
     9     private Set<Role> roles;
    10 }
    11 
    12 //角色
    13 @Data
    14 @Accessors(chain = true)
    15 public class Role {
    16     private Integer rId;
    17     private String role;
    18     private Set<Perm> perms;
    19 }
    20 
    21 //权限
    22 @Data
    23 @Accessors
    24 public class Perm {
    25     private Integer pId;
    26     private String perm;
    27 }

    3.dao层

     1 //@Mapper
     2 //如果添加了@MapperScann就可以不用配置@Mapper,要添加在Application中不是test
     3 public interface UserMapper extends BaseMapper<User> {
     4 
     5     @Select("select * from t_user where name=#{username}")
     6     @Results(id = "roleMap", value = {
     7             //因为采用了Mybatis的注解配置查询,字段名与属性名不相同,要指出
     8             @Result(property = "uId",column = "u_id",id = true),
     9             @Result(property = "roles", column = "u_id",
    10                     many = @Many(select = "com.chz.dao.RoleMapper.queryRole",
    11                             fetchType = FetchType.LAZY))
    12     })
    13     User queryUserRoles(@Param("username") String username);
    14 }
    15 
    16 
    17 public interface RoleMapper extends BaseMapper<Role> {
    18     //使用内嵌查询,User传过来uid通过第三张表拿到对应的r_id
    19     @Select("select * from t_role t where r_id = (select r_id from t_u_r ur where #{u_id} = ur.u_id)")
    20     @Results({
    21             @Result(property = "rId",column = "r_id",id = true),
    22             @Result(property = "perms",column = "r_id",
    23                     many = @Many(select = "com.chz.dao.PermMapper.queryPerms",
    24                     fetchType = FetchType.LAZY))
    25     })
    26     List<Role> queryRole(@Param("u_id") Integer uId);
    27 }
    28 
    29 public interface PermMapper {
    30     //然后通过传过来的r_id拿到对应的perm, 多个 perm用in
    31     @Select("select * from t_perm where p_id in (select p_id from t_r_p where r_id = #{r_id})")
    32     List<Perm> queryPerms(@Param("r_id") Integer rId);
    33 }

    4. service层

     1 @Service
     2 public class UserService extends ServiceImpl<UserMapper, User> implements IUserService {
     3     @Autowired
     4     UserMapper userMapper;
     5 
     6     //同样可以加在Service的方法上来限定用户权限
     7     // @RequiresRoles("admin")
     8     public User queryOne(String usernmae) {
     9         return userMapper.queryUserRoles(usernmae);
    10     }
    11     
    12     //可以通过shiro获取到session
    13     public void testShiroSession(){
    14         Session session = SecurityUtils.getSubject().getSession();
    15         System.out.println(session.getAttribute("sessionKey"));
    16     }
    17 }

    5. 编写自定义Realm

     1 /**
     2  * 继承AuthorzingRealm
     3  * 用户认证后执行权限认证与授权
     4  */
     5 public class CustomizeRealm1 extends AuthorizingRealm {
     6     @Autowired
     7     UserService userService;
     8 
     9     /**
    10      * 权限认证
    11      *
    12      * @param principals 即用户,底层是一个LinkedHashMap,先进先出
    13      * @return
    14      */
    15     @Override
    16     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    17         if (principals == null) {
    18             throw new UnknownAccountException();
    19         }
    20         SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
    21         //1. 从Principal中获取登入的用户信息,只有在需要权限认证时才会调用
    22         String username = principals.getPrimaryPrincipal().toString();
    23         //2. 查询数据库或缓存中的用户
    24         User users = userService.queryOne(username);
    25         for (Role role : users.getRoles()) {
    26             //获取角色,并给用户添加角色
    27             simpleAuthorizationInfo.addRole(role.getRole());
    28             for (Perm perm : role.getPerms()) {
    29                 //获取角色权限,并给用户添加权限
    30                 simpleAuthorizationInfo.addStringPermission(perm.getPerm());
    31             }
    32         }
    33         //3.返回
    34         return simpleAuthorizationInfo;
    35     }
    36 
    37     /**
    38      * 用户认证
    39      * MD5加密
    40      * @param token token就是controller中设置的controller
    41      * @return
    42      * @throws AuthenticationException
    43      */
    44     @Override
    45     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    46         System.out.println("[FirstRealm] doGetAuthenticationInfo");
    47         //1. 将AuthenticationToken 转为UsernamePasswordToken
    48         UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
    49         //2. 从UsernamepasswordToken中获取username,表单输入的username
    50         String username = usernamePasswordToken.getUsername();
    51         //3. 调用数据库方法,从数据库中查询出username对应的记录
    52         User user = userService.getOne(new QueryWrapper<User>().eq("name", username));
    53         //4. 用户不存在,则抛出异常UnknownAccountException
    54         // 有可能查询到的结果为null,需要判断一次
    55         if (null == user) {
    56             throw new UnknownAccountException();
    57         }
    58         //5. 根据用户信息,决定是否抛出其他的AuthenticationException异常
    59         if (Objects.equals(user.getName(), "老王")) {
    60             throw new LockedAccountException();
    61         }
    62         //6. 根据用户情况,来构建AuthenticationInfo对象,并返回
    63         //以下信息是从数据库中获取的,除第三点外
    64         //1). principal: 认证的实体信息,可以是usename,也可以是数据表对应的实体类对象
    65         String principal = user.getName();
    66         //2). credentials: 密码
    67         String credentials = user.getPassword();
    68         //3). 计算对应用户名的盐值,一般使用随机字符串或user id
    69         ByteSource salt = ByteSource.Util.bytes(username);
    70         //4). realmName: 当前realm对象的name,调用父类的getName()方法即可
    71         String realmName = getName();
    72         //7. 密码校验由shiro校验,抛到controller中
    73         //带上盐值比对数据库密码
    74         SimpleAuthenticationInfo simpleAuthenticationInfo =
    75                 new SimpleAuthenticationInfo(principal, credentials, salt, realmName);
    76         return simpleAuthenticationInfo;
    77     }
    78 }

    6. 编写shiro配置类

      1 @Configuration
      2 public class ShiroConf {
      3     /**
      4      * Shiro 过滤url,交给SecurityManager代理
      5      * 主要配置一些拦截的url,放行的url..
      6      *
      7      * @param manager 安全代理
      8      * @return
      9      */
     10     @Bean
     11     public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager manager) {
     12         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
     13         //必须使用LinkedHashMap否则会出现资源只能加载一次然后就被拦截的情况
     14         LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();
     15         /*
     16         观察DefaultFilter获取过滤器
     17         添加shiro内置过滤器,可以实现权限相关的拦截器,同样会拦截静态资源
     18         常用过滤器:
     19         1. anon: 无需认证(登入)可以访问资源  anonymous
     20         2. authc: 必须认证才能访问   authority
     21         3. user: 如果使用rememberMe的功能可以访问,同时认证通过的也可以访问
     22         4. perms: 该资源必须得到资源权限才能访问
     23         5. roles: 该资源必须得到角色权限才能访问
     24          */
     25         //anon的请求必须放在authc之上
     26         filterMap.put("/", "anon");
     27         filterMap.put("/index", "anon");
     28         filterMap.put("/login", "anon");
     29         filterMap.put("/logout", "anon");
     30         filterMap.put("/testSession", "anon");
     31         //一般通过数据库来设置用户权限
     32         //[]中的是角色,要求认证通过同时要有用户角色
     33         //filterMap.put("/add","auth,roles[user]");
     34         //[]中是权限
     35         //filterMap.put("/delete","perms[user:delete]");
     36         filterMap.put("/add", "user");
     37         filterMap.put("/update", "user");
     38         filterMap.put("/**", "authc");
     39         //set的这些url都会被shiro自动放行
     40         //设置未授权的提示页面,如果通过注解形式配置权限,setUnauthorizedUrl不会生效
     41         shiroFilterFactoryBean.setUnauthorizedUrl("/unAuthc");
     42         //如果当前site没有cookie就会跳转到该页面
     43         shiroFilterFactoryBean.setLoginUrl("/toLogin");
     44         //未生效
     45 //        shiroFilterFactoryBean.setSuccessUrl("/success");
     46         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
     47         //配置filter的代理
     48         shiroFilterFactoryBean.setSecurityManager(manager);
     49         return shiroFilterFactoryBean;
     50     }
     51 
     52     /**
     53      * SecurityManager: 关联Realm,Subject,执行Realm用户认证,权限认证
     54      *
     55      * @param modularRealmAuthenticator 多realm认证器
     56      * @param customizeRealm1           realm 1
     57      * @param customizeRealm2           realm 2
     58      * @return
     59      */
     60     @Bean
     61     public DefaultWebSecurityManager webSecurityManager(
     62             ModularRealmAuthenticator modularRealmAuthenticator,
     63             CustomizeRealm1 customizeRealm1, CustomizeRealm2 customizeRealm2,
     64             CookieRememberMeManager cookieRememberMeManager) {
     65         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
     66         //配置多realm认证器
     67         securityManager.setAuthenticator(modularRealmAuthenticator);
     68         List<Realm> authorizingRealms = Arrays.asList(customizeRealm1, customizeRealm2);
     69         securityManager.setRealms(authorizingRealms);
     70         //配置Cookie管理器
     71         securityManager.setRememberMeManager(cookieRememberMeManager);
     72         return securityManager;
     73     }
     74 
     75     /**
     76      * realm 1 采用MD5加密,用户只要满足其中一个realm即可认证
     77      * 用于用户认证,和授权
     78      *
     79      * @param matcher 指定的密码匹配器
     80      * @return
     81      */
     82     @Bean
     83     public CustomizeRealm1 customizeRealm1(
     84             @Qualifier("MD5") HashedCredentialsMatcher matcher) {
     85         CustomizeRealm1 customizeRealm1 = new CustomizeRealm1();
     86         //告诉Realm使用MD5加密
     87         customizeRealm1.setCredentialsMatcher(matcher);
     88         return customizeRealm1;
     89     }
     90 
     91     /**
     92      * realm 2 采用SHA1加密,用户只要满足其中一个realm即可认证
     93      * 用于用户认证,和授权
     94      */
     95     @Bean
     96     public CustomizeRealm2 customizeRealm2(
     97             @Qualifier("SHA1") HashedCredentialsMatcher matcher) {
     98         CustomizeRealm2 customizeRealm2 = new CustomizeRealm2();
     99         customizeRealm2.setCredentialsMatcher(matcher);
    100         return customizeRealm2;
    101     }
    102 
    103     /**
    104      * 开启多Realm认证,需要将该bean纳入SecurityManager管理
    105      * 这里的Realm其实是SecurityManager传过来的
    106      *
    107      * @param customizeRealm1
    108      * @param customizeRealm2
    109      * @return
    110      */
    111     @Bean
    112     public ModularRealmAuthenticator authenticator(
    113             CustomizeRealm1 customizeRealm1, CustomizeRealm2 customizeRealm2) {
    114         ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
    115         /*
    116         修改认证策略,默认AtLeastOneSuccessfulStrategy
    117         AtLeastOneSuccessfulStrategy,只要一个realm验证成功即可
    118         AllSuccessfulStrategy,要求所有realm验证成功
    119         FirstSuccessfulStrategy,只要第一个realm验证成功即可,子realm忽略
    120          */
    121 //        authenticator.setAuthenticationStrategy(new AllSuccessfulStrategy());
    122         authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
    123         //关联多个realm
    124         List<Realm> authorizingRealms = Arrays.asList(customizeRealm1, customizeRealm2);
    125         authenticator.setRealms(authorizingRealms);
    126         return authenticator;
    127     }
    128 
    129     /**
    130      * 开启注解配置权限必须添加如下两个bean
    131      *
    132      * @param securityManager 安全代理
    133      * @return
    134      * @RequiresPermission 必须由指定权限
    135      * @RequiresRole 必须具有指定角色
    136      * @RequiresAuthentication 已经通过Subject login的
    137      * @RequiresUser 已验证或者是已记住我的用户
    138      * 配置shiro与spring关联
    139      */
    140     @Bean
    141     public AuthorizationAttributeSourceAdvisor advisor(DefaultWebSecurityManager securityManager) {
    142         AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    143         advisor.setSecurityManager(securityManager);
    144         return advisor;
    145     }
    146 
    147     @Bean
    148     public DefaultAdvisorAutoProxyCreator creator() {
    149         DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
    150         creator.setProxyTargetClass(true);
    151         return creator;
    152     }
    153 
    154     /**
    155      * 开启shiro标签
    156      *
    157      * @return
    158      */
    159     @Bean
    160     public ShiroDialect shiroDialect() {
    161         return new ShiroDialect();
    162     }
    163 
    164     /**
    165      * MD5加密
    166      *
    167      * @return
    168      */
    169     @Bean
    170     public HashedCredentialsMatcher MD5() {
    171         HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
    172         //指定加密方式为MD5
    173         matcher.setHashAlgorithmName("MD5");
    174         //加密次数
    175         matcher.setHashIterations(10);
    176         matcher.setStoredCredentialsHexEncoded(true);
    177         return matcher;
    178     }
    179 
    180     /**
    181      * SHA1 加密
    182      *
    183      * @return
    184      */
    185     @Bean
    186     public HashedCredentialsMatcher SHA1() {
    187         HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
    188         //指定加密方式为SHA1
    189         matcher.setHashAlgorithmName("SHA1");
    190         //加密次数
    191         matcher.setHashIterations(10);
    192         matcher.setStoredCredentialsHexEncoded(true);
    193         return matcher;
    194     }
    195 
    196     /**
    197      * cookie
    198      *
    199      * @return
    200      */
    201     @Bean
    202     public SimpleCookie rememeberMeookie() {
    203         //对应前端的checkbox的name
    204         SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
    205         //如果httyOnly设置为true,则客户端不会暴露给客户端脚本代码,使用HttpOnly cookie有助于减少某些类型的跨站点脚本攻击;
    206         simpleCookie.setHttpOnly(true);
    207         //设置cookie最长存活时间,单位秒
    208         simpleCookie.setMaxAge(30);
    209         return simpleCookie;
    210     }
    211 
    212     /**
    213      * Cookie管理器
    214      *
    215      * @param cookie
    216      * @return
    217      */
    218     @Bean
    219     public CookieRememberMeManager cookieRememberMeManager(SimpleCookie cookie) {
    220         CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
    221         //rememberme cookie加密的密钥
    222         byte[] cipherKey = Base64.decode("wrjUh2ttBPQLnT4JVhriug==");
    223         cookieRememberMeManager.setCipherKey(cipherKey);
    224         cookieRememberMeManager.setCookie(cookie);
    225         return cookieRememberMeManager;
    226     }
    227 
    228 }

    7.编写controller层

     1 @Controller
     2 public class MyController {
     3     @Autowired
     4     private UserService userService;
     5 
     6     /**
     7      * 测试shiro service层调用session
     8      */
     9     @RequestMapping("/testSession")
    10     public String testShiroSession(HttpSession session) {
    11         session.setAttribute("sessionKey", "sessionValue");
    12         userService.testShiroSession();
    13         return "/index";
    14     }
    15 
    16 //    @RequiresRoles("admin")
    17     @GetMapping("/add")
    18     public String add() {
    19         return "list";
    20     }
    21 
    22 //    @RequiresRoles("admin")//只有指定的角色才有权限访问该uri
    23     @GetMapping("/delete")
    24     public String delete() {
    25         return "list";
    26     }
    27 
    28     @GetMapping("/update")
    29     public String update() {
    30         return "list";
    31     }
    32 
    33 //    @RequiresPermissions("add")//只有指定拥有权限才能访问该uri
    34     @GetMapping("/select")
    35     public String select() {
    36         return "list";
    37     }
    38 
    39     /**
    40      * shiro跳转页面
    41      */
    42     @GetMapping("/toLogin")
    43     public String toLogin() {
    44         return "login";
    45     }
    46 
    47     /**
    48      * 注销
    49      */
    50     @GetMapping("/logout")
    51     public String logout() {
    52         Subject currentUser = SecurityUtils.getSubject();
    53         currentUser.logout();
    54         return "login";
    55     }
    56 
    57     @PostMapping("/login")
    58     public String login(@RequestParam(required = false) String username,
    59                         @RequestParam(required = false) String password,
    60                         @RequestParam(required = false) Boolean rememberMe,
    61                         Model model) {
    62         //获取当前的用户
    63         Subject currentUser = SecurityUtils.getSubject();
    64         //关联用户凭证
    65         UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    66         try {
    67             //如果页面不是一些重要的用户信息,可以采用rememberMe,如果是重要信息不能设置rememberMe
    68             //记住我,会生成一个cookie,如果没有rememberMe默认是一个临时的cookie浏览器关闭就死亡
    69             if (null != rememberMe) {//checkbox中如果钩中就是true,如果没有钩中就是null
    70                 //需要在login之前设置
    71                 token.setRememberMe(true);
    72             }
    73             /*调用login实际是securityManager的login(),然后调用authenticate()
    74             如果是一个用户就会调用doSingleRealmAuthentication(),然后
    75             调用realm的doGetAuthenticationInfo(token),进行用户认证
    76             */
    77             currentUser.login(token);
    78             return "index";
    79         } catch (UnknownAccountException e) {
    80             model.addAttribute("msg", "用户名错误");
    81             return "login";
    82         } catch (IncorrectCredentialsException e) {
    83             model.addAttribute("msg", "密码错误");
    84             return "login";
    85         }
    86     }
    87 }

    8. 异常捕捉

     1 @ControllerAdvice
     2 public class ExceptionController {
     3     //通过注解形式设置role和perms,setUnauthorizedUrl不生效,要配置全局异常捕捉
     4     //捕捉权限认证失败
     5     @ExceptionHandler(AuthorizationException.class)
     6     @ResponseBody
     7     public String authorizationException() {
     8         return "你没有权限";
     9     }
    10 
    11     //捕捉多realm验证失败,注意是shiro的包
    12     @ExceptionHandler({AuthenticationException.class})
    13     @ResponseBody
    14     public String authenticationException(){
    15         return "认证失败";
    16     }
    17 }
  • 相关阅读:
    第一周。。。
    新人日报1129
    Daily Report-1126
    How to read source code[repost]
    Markdown tutorial [repost]
    蘑菇街面经
    阿里面经
    百度凤巢一二面经
    Mybatis最入门---代码自动生成(generatorConfig.xml配置)
    Maven的生命周期阶段
  • 原文地址:https://www.cnblogs.com/kikochz/p/12708592.html
Copyright © 2011-2022 走看看