+ Estructuras Shiro separados extremos frontal y posterior de la arquitectura basada en autenticación de inicio de primavera

I. Introducción

¿Por Shiro?

Apache Shiro es un potente y fácil de usar marco de seguridad de Java. Los desarrolladores que utilizan shiro pueden completar fácilmente la autenticación, autorización, contraseña y gestión de sesiones.

API principal de Shiro

Autenticación : autenticación / login, verificar que el usuario no se tiene la identificación apropiada;

La Autorización : Autorización que la competencia para verificar, verificar que un usuario se ha autenticado un privilegio, es decir, si el usuario puede hacer cosas tales como común: verificar que un usuario tiene un papel. O de grano fino verificar si un usuario tiene un permiso para que un recurso;

Director de la Sesión : Gestión de sesiones, es decir, después de que el usuario entre una sesión, en ausencia de salida, que es toda la información de la sesión, la sesión puede ser favorable al medio JavaSE ordinaria, que puede ser tan entorno Web;

Criptografía : cifrado, datos seguros, tales como contraseñas cifradas almacenadas en la base de datos, en lugar de almacenar el texto en claro;

Web de soporte : Soporte web se puede integrar fácilmente en el entorno web;

El almacenamiento en caché : caché, tales como los usuarios se conectan, su información de usuario, con papeles / permisos no tienen que comprobar cada vez, esto puede aumentar la eficiencia;

Concurrencia : aplicaciones multiproceso shiro apoyo concurrente de verificación, tales como la apertura que es otro hilo en un hilo, los permisos pueden ser propagadas de forma automática en el pasado;

Prueba de : proporcionar apoyo de prueba;

Como de RUN : permite a un usuario para pretender a otro usuario (si lo permiten) la identidad de acceso;

Recuérdame : Acuérdate de mí, esta es una característica muy común, es decir, después del primer inicio de sesión, entonces no volver la próxima vez conectado.

Cómo funcionan Shiro?

Asunto : sujeto, que representa el "usuario" actual, el usuario no es necesariamente una persona específica, y cualquier material actual es Asunto aplicaciones interactivas, tales como rastreadores web, robots, que es un concepto abstracto; todos están obligados al sujeto SecurityManager, todas las interacciones con el sujeto estará a cargo de la SecurityManager; el sujeto puede ser considerado una fachada; SecurityManager es el ejecutante real;

SecurityManager : gestor de seguridad, es decir, todas las operaciones relacionadas con la seguridad va a interactuar con el SecurityManager; y gestiona todas las que, podemos ver que es el núcleo de Shiro, que es responsable de la interacción con otros componentes detrás de la introducción de, si se estudia SpringMVC, se puede ver como controlador frontal DispatcherServlet;

Realm : dominio, Shiro adquirió de datos de seguridad (tales como usuarios, roles, permisos) de la Realm, que es SecurityManager a los usuarios autenticados, que necesita para obtener el usuario apropiado de la Realm se comparan para determinar la identidad del usuario es legítimo; necesidad de la Realm para dar al usuario el papel que / permisos para verificar que el usuario puede operar; Realm lata como dataSource, es decir, fuente de datos seguro.

 

II. El código del núcleo

1. Inicio de sesión / cierre de sesión función

Supongamos que tenemos dos usuarios:

 

Admin1: nombre de usuario / contraseña Admin1 / admin el éxito, la identidad y sessionId;

Admin2: nombre de usuario / contraseña Admin2 / admin el éxito, la identidad y sessionId;

Después de que el control requerido acceso a la interfaz de cinta cabecera solicitud debe cabecera sessionId (Autorización: formato sessionId) en http.

De conexión / desconexión Código:

@PostMapping("/login")
    public Object login(@RequestBody LoginVo loginVo) {
        //得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getAccount(), loginVo.getPassword());
        token.setRememberMe(false);
        if (subject.isAuthenticated()) {
            subject.logout();
        }
        try {
            //登录,即身份验证
            subject.login(token);
            Session session = subject.getSession();
            User user = User.loginUser();
            user.setFlag(loginVo.getFlag());
            user.setSessionId(session.getId());
            //返回一个sessionId
            return user;
        } catch (UnknownAccountException e){
            return "账号/密码错误";
        }
        catch (AuthenticationException e) {
            //身份验证失败
            return "程序错误";
        }
    }

    @PostMapping("/logout")
    public Object logout() {
        try {
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
            return "成功退出登录!";
        } catch (Exception e) {
            return "退出登录失败!";
        }
    }

 

arreglo 2.Shiro

soporte de anotaciones Shiro configurado interceptor, y @RequiresPermissions abiertas

package com.lee.config;


import com.lee.filter.ApiPathPermissionFilter;
import com.lee.shiro.MyRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 *
 * 功能描述: shiro配置类
 *
 * @param:
 * @return:
 * @auther: liyiyu
 * @date: 2020/3/17 17:05
 */
@Configuration
public class ShiroConfig {

    /**
     * 设置/login /logout 两个请求可以任意访问
     */
    @Bean
    public ShiroFilterFactoryBean factory(SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

        factoryBean.setSecurityManager(securityManager);

        // 自定义拦截器
        Map<String, Filter> filterMap = new LinkedHashMap<>();
        filterMap.put("apiPathPermissionFilter", new ApiPathPermissionFilter());
        factoryBean.setFilters(filterMap);

        Map<String, String> filterRuleMap = new LinkedHashMap<>();
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterRuleMap.put("/logout", "logout");
        // 配置不会被拦截的链接 顺序判断
        filterRuleMap.put("/login", "anon");
        // 其他请求通过我们自己的apiPathPermissionFilter
        filterRuleMap.put("/*", "apiPathPermissionFilter");
        filterRuleMap.put("/**", "apiPathPermissionFilter");
        filterRuleMap.put("/*.*", "apiPathPermissionFilter");
        factoryBean.setFilterChainDefinitionMap(filterRuleMap);

        return factoryBean;
    }

    /**
     * SecurityManager安全管理器,是Shiro的核心
     */
    @Bean
    public SecurityManager securityManager(MyRealm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置session生命周期
        securityManager.setSessionManager(sessionManager());
        // 设置自定义 realm
        securityManager.setRealm(myRealm);
        return securityManager;
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        // 强制使用cglib,防止重复代理和可能引起代理出错的问题
        // https://zhuanlan.zhihu.com/p/29161098
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /**
     * 开启aop注解支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * 自定义sessionManager
     */
    @Bean
    public SessionManager sessionManager() {
        //由shiro管理session,每次访问后会重置过期时间
        MySessionManager mySessionManager = new MySessionManager();
        //设置过期时间,单位:毫秒
        mySessionManager.setGlobalSessionTimeout(MySessionManager.DEFAULT_GLOBAL_SESSION_TIMEOUT);
        return mySessionManager;
    }


    /**
     * LifecycleBeanPostProcessor将Initializable和Destroyable的实现类统一在其内部,
     * 自动分别调用了Initializable.init()和Destroyable.destroy()方法,从而达到管理shiro bean生命周期的目的
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


}

2.sessionId get

Lograr su propio conjunto de gestor de sesiones, la necesidad de reescribir DefaultWebSessionManager
package com.lee.config;

import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

/**
 *
 * 功能描述: 自定义sessionId获取,用于请求头中传递sessionId,并让shiro获取判断权限
 * 想实现自己的一套session管理器,需继承DefaultWebSessionManager来重写
 *
 * @param:
 * @return:
 * @auther: liyiyu
 * @date: 2020/3/17 17:05
 */
public class MySessionManager extends DefaultWebSessionManager {

    private static final String AUTHORIZATION = "Authorization";

    public MySessionManager() {
        super();
    }


    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        //修改shiro管理sessionId的方式,改为获取请求头,前端时header必须带上 Authorization:sessionId
        return WebUtils.toHttp(request).getHeader(AUTHORIZATION);
    }
}

3. Acceder a lograr y control de acceso

doGetAuthenticationInfo es un inicio de sesión aplicación concreta de juzgar mediante la consulta del partido contraseña de base de datos y encriptación de la cuenta es correcta

doGetAuthorizationInfo juicio permiso se materializa para determinar la petición del usuario mediante la adquisición de los permisos de usuario actuales

Hemos establecido permisos Admin1 poetry1 poesía2 de, Admin2 tiene derechos poetry4 poetry3

package com.lee.shiro;

import com.lee.entity.User;
import com.lee.util.PasswordUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 *
 * 功能描述:自定义用户认证授权类
 *
 * @param:
 * @return:
 * @auther: liyiyu
 * @date: 2020/3/17 17:04
 */
@Component
public class MyRealm extends AuthorizingRealm {

    @Value("${password_salt}")
    private String salt;

    /**
     * AuthorizationInfo 用于聚合授权信息
     * 会判断@RequiresPermissions 里的值是否
     *
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //进入数据库查询拥有的权限查询
        List<String> list = new ArrayList<>();
        User user = (User)SecurityUtils.getSubject().getPrincipal();
        if ("1".equals(user.getRoleId())){
            String[] array = {"poetry1","poetry2"};
            list  = Arrays.asList(array);
        }else if ("2".equals(user.getRoleId())){
            String[] array = {"poetry3","poetry4"};
            list  = Arrays.asList(array);
        }

        Set<String> set = new HashSet(list);
        info.addStringPermissions(set);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String account = (String) authenticationToken.getPrincipal();  //得到用户名
        String pwd = new String((char[]) authenticationToken.getCredentials()); //得到密码
        //将密码进行加密处理,与数据库加密后的密码进行比较
        String inPasswd = PasswordUtils.entryptPasswordWithSalt(pwd, salt);
        //通过数据库验证账号密码,成功的话返回一个封装的ShiroUser实例
        String saltPasswd = PasswordUtils.entryptPasswordWithSalt("admin", salt);
        User user = null;
        //这里要注意返回用户信息尽可能少,返回前端所需要的用户信息就可以了
        if ("admin1".equals(account) && saltPasswd.equals(inPasswd)) {
            user = new User();
            user.setUid("1");
            user.setUname("用户一");
            user.setEid(1);
            user.setDeptName("祖安大区");
            user.setDeptId("1");
            user.setRoleId("1");
            user.setRoleName("祖安文科状元");
        }else if ("admin2".equals(account) && saltPasswd.equals(inPasswd)){
            user = new User();
            user.setUid("1");
            user.setUname("用户二");
            user.setEid(1);
            user.setDeptName("祖安大区");
            user.setDeptId("1");
            user.setRoleId("2");
            user.setRoleName("祖安理科状元");
        }
        if (user != null) {
            //如果身份认证验证成功,返回一个AuthenticationInfo实现;
            return new SimpleAuthenticationInfo(user, pwd, getName());
        } else {
            //错误的帐号
            throw new UnknownAccountException();
        }
    }
}

4. Las cuestiones entre dominios

La aparición de problemas entre dominios en el proceso de desarrollo

package com.lee.config;

import com.lee.interceptor.UrlInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 *
 * 功能描述: MVC拦截器配置
 *
 * @param:
 * @return:
 * @auther: liyiyu
 * @date: 2020/3/17 17:06
 */
@Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //为所有请求处理跨域问题
        registry.addInterceptor(new UrlInterceptor()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }

}

soluciones entre dominios

package com.lee.interceptor;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 * 功能描述: url拦截器,处理前后端分离跨域问题
 *
 * @param:
 * @return:
 * @auther: liyiyu
 * @date: 2020/3/17 17:04
 */
public class UrlInterceptor extends HandlerInterceptorAdapter {

	@Override
	public boolean preHandle(HttpServletRequest request,
							 HttpServletResponse response, Object handler) throws Exception {
		//允许跨域,不能放在postHandle内
		response.setHeader("Access-Control-Allow-Credentials", "true");
		String str = request.getHeader("origin");
		response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));
		response.setHeader("Cache-Control", "no-cache");
		response.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT");
		response.setHeader("Access-Control-Max-Age", "0");
		response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept,Authorization,WG-App-Version, WG-Device-Id, WG-Network-Type, WG-Vendor, WG-OS-Type, WG-OS-Version, WG-Device-Model, WG-CPU, WG-Sid, WG-App-Id, WG-Token");
		response.setHeader("XDomainRequestAllowed", "1");
		return true;
	}
}

5. Verificar permisos

Para comprobar los permisos @RequiresPermissions (configuración de clase ShiroConfig)

    @GetMapping("/poetry1")
    @RequiresPermissions("poetry1")
    public Object poetry1(){
        return "床前明月光";
    }

    @GetMapping("/poetry2")
    @RequiresPermissions("poetry2")
    public Object poetry2(){
        return "疑是地上霜";
    }

    @GetMapping("/poetry3")
    @RequiresPermissions("poetry3")
    public Object poetry3(){
        return "举头望明月";
    }

    @GetMapping("/poetry4")
    @RequiresPermissions("poetry4")
    public Object poetry4(){
        return "低头思故乡";
    }

código específico: https://github.com/leeyiyu/shiro_token

Documentos de referencia: https://www.iteye.com/blog/jinnianshilongnian-2018398

Publicó un artículo original · ganado elogios 1 · vistas 39

Supongo que te gusta

Origin blog.csdn.net/liyiyu123/article/details/105016859
Recomendado
Clasificación