利用Shiro实现登录功能

技术文档网 2021-04-25

Shiro简介

Refer: https://shiro.apache.org/

Features

Shiro Features

  • Authentication: Sometimes referred to as ‘login’, this is the act of proving a user is who they say they are.

  • Authorization: The process of access control, i.e. determining ‘who’ has access to ‘what’.

  • Session Management: Managing user-specific sessions, even in non-web or EJB applications.

  • Cryptography: Keeping data secure using cryptographic algorithms while still being easy to use.

架构

High Level

High Level

  • Subject:用户主体

  • SecurityManager: Shiro核心, 管理所有的Subject 关联Realm

    • Authentication
    • Authorization
    • Session Management
    • Cache Management
    • Realm coordination
    • Event propagation
    • “Remember Me” Services
    • Subject creation
    • Logout and more.
  • Realm: Shiro与加密数据数据的连接桥梁

Detail

Detail Arch

Spring Boot与Shiro开发

导入依赖

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>

编写Shiro配置类

定义Realm类

@Component
public class UserRealm extends AuthorizingRealm  {
    public UserRealm() {
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        return null;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
}

定义SecurityManager

    @Bean
    public SecurityManager securityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 配置 SecurityManager,并注入 shiroRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

定义Filter

Shiro内置Filter,可以实现权限相关的拦截器

常用Filter:

anon: 无需认证登录可以访问

authc:必须认证才可以访问

user:如果使用rememberMe的功能可以直接访问

perms:该资源必须得到资源权限才可以访问

role:该资源必须得到角色权限才可以访问

过滤器简称 对应的java类
anon org.apache.shiro.web.filter.authc.AnonymousFilter
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port org.apache.shiro.web.filter.authz.PortFilter
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl org.apache.shiro.web.filter.authz.SslFilter
user org.apache.shiro.web.filter.authc.UserFilter
logout org.apache.shiro.web.filter.authc.LogoutFilter
    /**
     * ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        val shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        val filterChainDefinitionMap = new LinkedHashMap<String, String>(); // order
        filterChainDefinitionMap.put("/v1.0/session", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

用户登录

UserManagerImpl login

    @Override
    public void login(String username, String password) {
        // Get Subject
        val subject = SecurityUtils.getSubject();

        // Generate Token
        val token = new UsernamePasswordToken(username, password);

        subject.login(token);
    }

SessionController

@RestController
@RequestMapping("v1.0/session")
public class SessionControllerV1 {
    @Autowired
    private UserInfoManager userInfoManager;

    @PostMapping()
    public String login(@RequestParam("username") String username,
                                         @RequestParam("password") String password) {

        userInfoManager.login(username, password);

        return "success";
    }
}

UserRealm doGetAuthenticationInfo

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    String username = (String) token.getPrincipal();
    String password = new String((char[]) token.getCredentials());

    if (username == null || username.isEmpty() || !username.equals("hardcore")) {
        throw new UnknownAccountException("unknown");
    }
    if (!password.equals("hardcore")) {
        throw new IncorrectCredentialsException("password is invalid");
    }
    return new SimpleAuthenticationInfo(username, password, null, this.getName());
}

Login过程源码分析

连接数据库

从数据库中获取信息

doGetAuthenticationInfo中通过userManager

String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());

        val userInfo = userInfoManager.getUserInfoByUserId(1L);

        if (!password.equals(userInfo.getPassword())) {
            throw new IncorrectCredentialsException("password is invalid");
        }
        return new SimpleAuthenticationInfo(userInfo.getUsername(), userInfo.getPassword(), null, this.getName());
POST http://localhost:8080/users/login
{
    "username": "zg",
    "password": "123456,
}

Output: success

相关文章

  1. 基于-SLF4J-MDC-机制的日志链路追踪配置属性

    ums: # ================ 基于 SLF4J MDC 机制的日志链路追踪配置属性 ================ mdc: # 是否支持基于 SLF4J MDC

  2. ajax-跨域访问

    ajax 跨域访问 &lt;!DOCTYPE html&gt; &lt;html xmlns:th="http://www.w3.org/1999/xhtml"&gt; &lt;head&gt;

  3. 给第三方登录时用的数据库表-user_connection-与-auth_token-添加-redis-cache

    spring: # 设置缓存为 Redis cache: type: redis # redis redis: host: 192.168.88.88 port

  4. Java动态代理

    Jdk动态代理 通过InvocationHandler和Proxy针对实现了接口的类进行动态代理,即必须有相应的接口 应用 public class TestProxy { public

  5. Java读取classpath中的文件

    public void init() { try { //URL url = Thread.currentThread().getContextClassLo

随机推荐

  1. 基于-SLF4J-MDC-机制的日志链路追踪配置属性

    ums: # ================ 基于 SLF4J MDC 机制的日志链路追踪配置属性 ================ mdc: # 是否支持基于 SLF4J MDC

  2. ajax-跨域访问

    ajax 跨域访问 &lt;!DOCTYPE html&gt; &lt;html xmlns:th="http://www.w3.org/1999/xhtml"&gt; &lt;head&gt;

  3. 给第三方登录时用的数据库表-user_connection-与-auth_token-添加-redis-cache

    spring: # 设置缓存为 Redis cache: type: redis # redis redis: host: 192.168.88.88 port

  4. Java动态代理

    Jdk动态代理 通过InvocationHandler和Proxy针对实现了接口的类进行动态代理,即必须有相应的接口 应用 public class TestProxy { public

  5. Java读取classpath中的文件

    public void init() { try { //URL url = Thread.currentThread().getContextClassLo