SSM框架多模块+ElementUI+VUE前后端分离开发集成shiro做登录

SSM框架多模块+ElementUI+VUE前后端分离开发集成shiro做登录

分析登录流程

SSM框架多模块+ElementUI+VUE前后端分离开发集成shiro做登录_第1张图片
前端发起请求携带用户名密码作为参数传到后端,接受之后使用shiro进行登录认证,并且拿到sessionId,返回给前端,前端得到sessionId后存储在前端的sessionStorage中,设置过滤器,每次发送请求时,请求头都携带sessionStorage中的sessionId,后端再次接收到请求时shiro先进行过滤,如果是跨域请求直接放行,后端配置sessionManager拿到前端发送过来的sessionId去找登录时的session对象,进行匹配,匹配成功拿到登录凭证。

代码实现

1.搭建子模块crm-shiro

pom.xml中导入依赖

	<dependency>
      <groupId>cn.xh.crm</groupId>
      <artifactId>crm-service</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
    <!-- 引入shiro的jar包-->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-all</artifactId>
      <version>1.4.1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore -->
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpcore</artifactId>
      <version>4.4.10</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
      <version>4.5.6</version>
    </dependency>

2.配置application-shiro.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="CrmSessionManager" class="cn.xh.crm.sessionmanager.CrmSessionManager"></bean>

    <!--shiro的核心对象 realm-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--配置realm的对象-->
        <property name="realm" ref="authRealm"/>
        <!--配置自定义Session管理器-->
        <property name="sessionManager" ref="CrmSessionManager"/>
    </bean>

    <!--自定义的Realm-->
    <bean id="authRealm" class="cn.xh.crm.realm.MyRealm">
        <!--密码匹配器-->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--加密方式-->
                <property name="hashAlgorithmName" value="MD5"/>
                <!--加密次数-->
                <property name="hashIterations" value="10"/>
            </bean>
        </property>
    </bean>

    <!--自定义的认证过滤器-->
    <bean id="MyCrmAuthentication" class="cn.xh.crm.filter.MyAuthenticationFilter"></bean>

    <!--shiro的过滤器配置-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!--认证不通过跳转的url-->
        <property name="loginUrl" value="http://localhost:8080/#/login"/>
        <!--认证通过跳转的url-->
        <property name="successUrl" value="http://localhost:8080/#/"/>
        <property name="filters">
            <map>
                <!--配置自定义认证过滤器-->
                <entry key="myAuthc" value-ref="MyCrmAuthentication"></entry>
            </map>
        </property>

        <!--权限map给到过滤器-->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
    </bean>
    <!--通过动态获取授权类调用它的createFilterChainDefinitionMap方法 获取权限map-->
    <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder"
          factory-method="createFilterChainDefinitionMap"/>
    <!--引入动态获取授权类-->
    <bean id="filterChainDefinitionMapBuilder" class="cn.xh.crm.realm.FilterChainDefinitionMapBuilder"/>

</beans>

3.实例工厂模式配置过滤器链filterChainDefinitionMap

public class FilterChainDefinitionMapBuilder {
	//权限
    @Autowired
    private IPermissionService permissionService;

    public Map<String,String> createFilterChainDefinitionMap(){
        Map<String,String> map = new LinkedHashMap<>();
        map.put("/login","anon");
        //查询数据库中权限对应的资源路径
        List<Permission> permissions = permissionService.selectAll();
        //遍历,添加
        for (Permission permission : permissions) {
            map.put(permission.getUrl(),"perms["+permission.getSn()+"]");
        }
        map.put("/**","myAuthc");
        return map;
    }
}

在application-shiro.xml中配置实例工厂配置的过滤器链

<!--shiro的过滤器配置-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!--认证不通过跳转的url-->
        <property name="loginUrl" value="http://localhost:8080/#/login"/>
        <!--认证通过跳转的url-->
        <property name="successUrl" value="http://localhost:8080/#/"/>
        <property name="filters">
            <map>
                <!--配置自定义认证过滤器-->
                <entry key="myAuthc" value-ref="MyCrmAuthentication"></entry>
                <!--配置自定义权限过滤器-->
                <!--<entry key="MyPerm" value-ref="MyPermissionsAuthorizationFilter"></entry>-->
            </map>
        </property>

        <!--权限map给到过滤器-->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
    </bean>
    <!--通过动态获取授权类调用它的createFilterChainDefinitionMap方法 获取权限map-->
    <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder"
          factory-method="createFilterChainDefinitionMap"/>
    <!--引入动态获取授权类-->
    <bean id="filterChainDefinitionMapBuilder" class="cn.xh.crm.realm.FilterChainDefinitionMapBuilder"/>

4.编写Controller

@Controller
@CrossOrigin
public class LoginController{

    @Autowired
    private IEmployeeService service;

    @RequestMapping(value = "/login",method = RequestMethod.POST)
    @ResponseBody
    public JSONResult login(@RequestBody Employee employee){
        Subject subject = null;
        try {
        	//获取主体对象
            subject = SecurityUtils.getSubject();
            //获取前端传入的用户名,密码
            UsernamePasswordToken token = new UsernamePasswordToken(employee.getUserName(),employee.getPassWord());
            subject.login(token);
        }catch (UnknownAccountException e){
            e.printStackTrace();
            return JSONResult.error("用户名或密码错误!");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            return JSONResult.error("用户名或密码错误!");
        }catch (AuthenticationException e){
            e.printStackTrace();
            return JSONResult.error("系统异常!");
        }
        employee.setPassWord("");
        Map<String,Object> map = new HashMap<>();
        map.put("user",employee);
        //将sessionId传给前端
        map.put("sessionId",subject.getSession().getId());
        return JSONResult.ok(map);
    }
}

5.自定义Realm

public class MyRealm extends AuthorizingRealm {
    @Autowired
    private IEmployeeService employeeService;

    @Autowired
    private IPermissionService permissionService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取当前登录对象
        Employee employee =(Employee) principalCollection.getPrimaryPrincipal();
        //查询该用户拥有的权限
        Set<String> permissionssns =
                permissionService.findPermissionssnsByLoginUser(employee.getId());
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //授权
        authorizationInfo.setStringPermissions(permissionssns);
        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //获取token
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        //获取用户名
        String username = token.getUsername();
        //查询数据库中是否有该用户
        Employee employee = employeeService.selectByUsername(username);

        if (employee != null){
            return new SimpleAuthenticationInfo(employee,employee.getPassWord(), ByteSource.Util.bytes(employee.getSalt()),"MyRealm");
        }
        return null;
    }
}

在application-shiro.xml中配置自定义的Realm

<!--自定义的Realm-->
    <bean id="authRealm" class="cn.xh.crm.realm.MyRealm">
        <!--密码匹配器-->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--加密方式-->
                <property name="hashAlgorithmName" value="MD5"/>
                <!--加密次数-->
                <property name="hashIterations" value="10"/>
            </bean>
        </property>
    </bean>

6.前端Login.vue得到返回的数据后存储sessionId

this.$http.post("/login",loginParams).then(res=>{
  if(res.data.success){
    sessionStorage.setItem('user', JSON.stringify(res.data.data.user));
    //存储sessionId
    sessionStorage.setItem('sessionId',res.data.data.sessionId);
    this.$router.push({ path: '/echarts' });
    this.logining = false;
  }else {
    this.$message.error(res.data.message);
    this.logining = false;
  }

}).catch(error => {
  console.log(error);
  this.$message.error(error);
  this.logining = false;
})

7.main.js中添加axios拦截器,每个前端请求头中都添加sessionId

//axios的拦截器 每次发送ajax请求时会经过该拦截器
axios.interceptors.request.use(config => {
 /*判断用户是否已登录*/
 if (sessionStorage.getItem('sessionId')) {
   /*把jsessionId 命名为X-TOKEN 返回给后台,告诉shiro已经登录过*/
   config.headers['X-TOKEN'] = sessionStorage.getItem('sessionId')
 }
 console.debug("config",config);
 return config
}, error => {
 // Do something with request error
 Promise.reject(error)
})

8.后端shiro解决跨域问题,配置自定义过滤器继承FormAuthenticationFilter,覆写isAccessAllowed方法

public class MyAuthenticationFilter extends FormAuthenticationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        HttpServletRequest servletRequest = (HttpServletRequest)request;
        String method = servletRequest.getMethod();
        //如果是跨域请求直接放行
        if ("OPTIONS".equals(method)){
            return true;
        }
        //如果不是跨域请求,执行父类方法
        return super.isAccessAllowed(request, response, mappedValue);
    }
}

在application-shiro.xml中引用自定义的过滤器

<!--shiro的过滤器配置-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!--认证不通过跳转的url-->
        <property name="loginUrl" value="http://localhost:8080/#/login"/>
        <!--认证通过跳转的url-->
        <property name="successUrl" value="http://localhost:8080/#/"/>
        <property name="filters">
            <map>
                <!--配置自定义认证过滤器-->
                <entry key="myAuthc" value-ref="MyCrmAuthentication"></entry>
                <!--配置自定义权限过滤器-->
                <!--<entry key="MyPerm" value-ref="MyPermissionsAuthorizationFilter"></entry>-->
            </map>
        </property>

        <!--权限map给到过滤器-->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
    </bean>

9.配置session管理器

public class CrmSessionManager extends DefaultWebSessionManager {

    private static final String AUTHORIZATION = "X-Token";

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public CrmSessionManager() {
        super();
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        //取到jessionid
        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        HttpServletRequest request1 = (HttpServletRequest) request;
        //如果请求头中有 X-TOKEN 则其值为sessionId
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //否则按默认规则从cookie取sessionId
            return super.getSessionId(request, response);
        }
    }
}

在application-shiro.xml中配置bean,在shiro核心对象中引用

 <bean id="CrmSessionManager" class="cn.xh.crm.sessionmanager.CrmSessionManager"></bean>
 <!--shiro的核心对象 realm-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--配置realm的对象-->
        <property name="realm" ref="authRealm"/>
        <!--配置自定义Session管理器-->
        <property name="sessionManager" ref="CrmSessionManager"/>
    </bean>

你可能感兴趣的:(shiro,java,spring)