Lxxxxxxy_
Lxxxxxxy_
Published on 2019-08-14 / 35 Visits
0

Shiro学习笔记

Shiro学习笔记

入门代码


    public void ShiroDemoByIni(){
        IniSecurityManagerFactory iniSecurityManagerFactory = new IniSecurityManagerFactory("classpath:user.ini");
        SecurityManager instance = iniSecurityManagerFactory.getInstance();
        SecurityUtils.setSecurityManager(instance);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("lxy", "lxy");
        subject.login(usernamePasswordToken);
        System.out.println(subject.isAuthenticated());
    }

ini配置

示例代码:

[users]
lxy=lxy,user
xlr=xlr,admin,user
[roles]
user="user:update,select"

知识点:

[users]下面配置用户的格式:用户名=密码,角色1,角色2,角色3......,比如xlr=xlr,admin,user

[roles]下面配置角色的格式:

1、角色=权限1,权限2,权限3......,比如admin=user:update,user:select,即admin拥有对user的update和select操作权限

2、角色="权限1,权限2,权限3......",比如admin="user:update,select",即admin拥有对user的update和select操作权限

3、角色=权限1:*,比如admin=user:*,即admin拥有对user的所有操作权限

4、角色=*:权限,比如或者admin=*:update,即admin拥有对所有资源的update权限

“:”表示资源/操作/实例的分割
“,”表示操作的分割
“*”表示任意资源/操作/实例
如“user:*”可以匹配如“user:delete”、“user:delete”可以匹配如“user:delete:1”、
“user:*:1”可以匹配如“user:view:1”、“user”可以匹配“user:view”或“user:view:1”
等。即*可以匹配所有,不加*可以进行前缀匹配;但是如“*:view”不能匹配
“system:user:view”,需要使用“*:*:view”,即后缀匹配必须指定前缀(多个冒号就需要
多个*来匹配)

未来MD5加密的实现方法

使用用户名和密码来进行MD5加密,也可以使用UUID和密码来进行MD5加密。

对SimpleAuthenticationInfo的思考

在自定义Realm中有这样一行代码:

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        User user = userService.getUser(username);
        if (user == null) {
            return null;
        }
    	//传入从数据库中取出来的用户名和密码
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),"custom");
        return simpleAuthenticationInfo;
    }

​ 在new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),"custom");代码中,是如何做到不需要token来验证用户输入的用户名和密码是否正确?


生成 AuthenticationInfo 信息,交给间接父类 AuthenticatingRealm 使用 CredentialsMatcher 进行判断密码是否匹配,在下面的java代码中:

public class PasswordMatcher implements CredentialsMatcher {

    private PasswordService passwordService;

    public PasswordMatcher() {
        this.passwordService = new DefaultPasswordService();
    }

    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {

        PasswordService service = ensurePasswordService();

        Object submittedPassword = getSubmittedPassword(token);
        Object storedCredentials = getStoredPassword(info);
        assertStoredCredentialsType(storedCredentials);

        if (storedCredentials instanceof Hash) {
            Hash hashedPassword = (Hash)storedCredentials;
            HashingPasswordService hashingService = assertHashingPasswordService(service);
            return hashingService.passwordsMatch(submittedPassword, hashedPassword);
        }
        //otherwise they are a String (asserted in the 'assertStoredCredentialsType' method call above):
        String formatted = (String)storedCredentials;
        return passwordService.passwordsMatch(submittedPassword, formatted);
    }

submittedPassword和storedCredentials分别是用户输入的密码和数据库查询到的密码,然后调用passwordService.passwordsMatch来验证。


设置对静态资源不拦截

不能够设置:

filterChainDefinitionMap.put("/static/**", "anon");

应该设置成:

filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");

Shiro的RequiresPermissions等注解不生效

在ShiroConfig中加上这段代码:

	@Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public MyExceptionResolver myExceptionResolver() {
        return new MyExceptionResolver();
    }

shiroFilterFactoryBean.setUnauthorizedUrl("/4xx.html");不生效

方法一:创建一个class类:

public class MyExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        if (e instanceof UnauthorizedException) {
            ModelAndView mv = new ModelAndView("/4xx");
            return mv;
        }
        return null;
    }
}

在ShiroConfig中加上:

	@Bean
    public MyExceptionResolver myExceptionResolver() {
        return new MyExceptionResolver();
    }

方法二:

在ShiroConfig中加上

@Bean
    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver simpleMappingExceptionResolver=new SimpleMappingExceptionResolver();
        Properties properties=new Properties();
        //这里的 /unauthorized 是页面,不是访问的路径
  properties.setProperty("org.apache.shiro.authz.UnauthorizedException","unauthorized");
        properties.setProperty("org.apache.shiro.authz.UnauthenticatedException","unauthorized");
        simpleMappingExceptionResolver.setExceptionMappings(properties);
        return simpleMappingExceptionResolver;
    }

Thymeleaf中使用Shiro标签

在pom.xml中加入:

		<dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

在html中加入:

xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"

Shiro标签不生效

在ShiroConfig中加入:

	@Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }

Shiro的@RequiresPermissions注解任选其一

在注解上加上:

@RequiresPermissions(value = {"admin:add","user:add"},logical= Logical.OR)

Shiro中用户注册加密和用户登录加密

用户注册加密

String md5 = new SimpleHash("md5", user.getPassword(), user.getUsername(), 5).toHex();
        user.setPassword(md5);
SimpleHash(加密算法,加密对象,加密盐值,加密次数)。

在获取到user对象后,即可设置加密。

用户登录加密

在ShiroConfig中配置Bean:

//设置在登录时自动将密码加密
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //设置加密算法
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //设置加密次数
        hashedCredentialsMatcher.setHashIterations(5);
        return hashedCredentialsMatcher;
    }

在CustomRealm中设置:

SimpleAuthenticationInfo custom = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), "custom");
        //设置用户加密的盐值,这里使用的是用户名
        custom.setCredentialsSalt(ByteSource.Util.bytes(user.getUsername()));
        return custom;

设置自动登录后设置不能访问login页

@GetMapping(value = "/login")
    public String skipLogin(){
        Subject subject = SecurityUtils.getSubject();
        //设置自动登录后设置不能访问login页(判断是否登录,如果登录了,访问login页时就直接跳转到list页面)
        if(subject.isAuthenticated()){
            return "redirect:/list";
        }else{
            return "/login";
        }
    }