CAS-技术专区-客户端集成(shiro springboot jwt pac4j)

导读:本篇文章讲解 CAS-技术专区-客户端集成(shiro springboot jwt pac4j),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

使用场景:

  • 移动端通过业务系统鉴权
  • 移动端免登录(登录一次以后)

解决方案:

  • JWT(token认证方案)
  • OAuth(第三方认证)

疑问

  当然我们这章是讲JWT,那么会有以下的疑问:

  若服务端已经接入了SSO,那么在移动端用户登录信息提交给SSO还是服务端?(毫无疑问是服务端,SSO对于移动端必须是透明的)

  若采用无会话方式,如何获取token,服务端如何鉴权?

1.提交用户名密码到服务端,服务端把数据给到sso,sso最终返回用户数据

2. 根据用户数据创建token返回给移动端

3. 移动端登录后请求都带token给到服务端鉴权

  在使用token鉴权的情况下,退出如何解决?(客户端丢弃token即可)

鉴权流程

  我们再讲一下整一个鉴权流程

CAS-技术专区-客户端集成(shiro springboot jwt pac4j)

配置要素

  重点:sso必须支持rest认证方式 

<dependency>
  <groupId>io.buji</groupId>
  <artifactId>buji-pac4j</artifactId>
  <version>3.0.0</version>
</dependency>
<dependency>
  <groupId>org.pac4j</groupId>
  <artifactId>pac4j-cas</artifactId>
  <version>2.1.0</version>
</dependency>
<dependency>
  <groupId>org.pac4j</groupId>
  <artifactId>pac4j-jwt</artifactId>
  <version>2.1.0</version>
</dependency>
<dependency>
  <groupId>org.pac4j</groupId>
  <artifactId>pac4j-http</artifactId>
  <version>2.1.0</version>
</dependency>

鉴权配置

若马上看下面的代码,估计一时半会看不懂,所以必须再讲一下整个交易过程

服务端鉴权过程有两个个角色分别为,Shiro、Pac4j,那他们的职责是什么?

Shiro:

  判断当前Subject是否有权限执行该资源,所以他的核心是Realm、Filter,只有被过滤到的资源才会走到Realm

Pac4j:

1. JWTAuthenticator对token进行鉴别
2. CasRestFormClient 支持通过rest接口传入用户名密码进行对sso进行认证获取UserProfile
3. Pac4jRealm鉴权的realm
4. SubjectFactory需要调整成Pac4jSubjectFactory

ShiroConfiguration.java

/*
 * 版权所有.(c)2008-2017. 卡尔科技工作室
 */
package com.carl.wolf.permission.config;
import io.buji.pac4j.filter.CallbackFilter;
import io.buji.pac4j.filter.LogoutFilter;
import io.buji.pac4j.filter.SecurityFilter;
import io.buji.pac4j.realm.Pac4jRealm;
import io.buji.pac4j.subject.Pac4jSubjectFactory;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SubjectFactory;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.AbstractShiroWebFilterConfiguration;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.pac4j.cas.client.CasClient;
import org.pac4j.cas.client.rest.CasRestFormClient;
import org.pac4j.cas.config.CasConfiguration;
import org.pac4j.cas.config.CasProtocol;
import org.pac4j.core.client.Clients;
import org.pac4j.core.config.Config;
import org.pac4j.http.client.direct.ParameterClient;
import org.pac4j.jwt.config.encryption.SecretEncryptionConfiguration;
import org.pac4j.jwt.config.signature.SecretSignatureConfiguration;
import org.pac4j.jwt.credentials.authenticator.JwtAuthenticator;
import org.pac4j.jwt.profile.JwtGenerator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * 对shiro的安全配置,是对cas的登录策略进行配置
 *
 * @author Carl
 * @date 2017/9/16
 * @since 1.0.0
 */
@Configuration
public class ShiroConfiguration extends AbstractShiroWebFilterConfiguration {
    
   @Value("#{ @environment['cas.prefixUrl'] ?: null }") private String prefixUrl; @Value("#{ @environment['cas.loginUrl'] ?: null }") private String casLoginUrl; @Value("#{ @environment['cas.callbackUrl'] ?: null }") private String callbackUrl; //jwt秘钥 @Value("${jwt.salt}") private String salt; @Bean public Realm pac4jRealm() { return new Pac4jRealm(); } /** * cas核心过滤器,把支持的client写上,filter过滤时才会处理,clients必须在casConfig.clients已经注册 * * @return */ @Bean public Filter casSecurityFilter() { SecurityFilter filter = new SecurityFilter(); filter.setClients("CasClient,rest,jwt"); filter.setConfig(casConfig()); return filter; } /** * JWT Token 生成器,对CommonProfile生成然后每次携带token访问 * @return */ @Bean protected JwtGenerator jwtGenerator() { return new JwtGenerator(new SecretSignatureConfiguration(salt), new SecretEncryptionConfiguration(salt)); } /** * 通过rest接口可以获取tgt,获取service ticket,甚至可以获取CasProfile * @return */ @Bean protected CasRestFormClient casRestFormClient() { CasRestFormClient casRestFormClient = new CasRestFormClient(); casRestFormClient.setConfiguration(casConfiguration()); casRestFormClient.setName("rest"); return casRestFormClient; } @Bean protected Clients clients() { //可以设置默认client Clients clients = new Clients(); //token校验器,可以用HeaderClient更安全 ParameterClient parameterClient = new ParameterClient("token", jwtAuthenticator()); parameterClient.setSupportGetRequest(true); parameterClient.setName("jwt"); //支持的client全部设置进去 clients.setClients(casClient(), casRestFormClient(), parameterClient); return clients; } /** * JWT校验器,也就是目前设置的ParameterClient进行的校验器,是rest/或者前后端分离的核心校验器 * @return */ @Bean protected JwtAuthenticator jwtAuthenticator() { JwtAuthenticator jwtAuthenticator = new JwtAuthenticator(); jwtAuthenticator.addSignatureConfiguration(new SecretSignatureConfiguration(salt)); jwtAuthenticator.addEncryptionConfiguration(new SecretEncryptionConfiguration(salt)); return jwtAuthenticator; } @Bean protected Config casConfig() { Config config = new Config(); config.setClients(clients()); return config; } /** * cas的基本设置,包括或url等等,rest调用协议等 * @return */ @Bean public CasConfiguration casConfiguration() { CasConfiguration casConfiguration = new CasConfiguration(casLoginUrl); casConfiguration.setProtocol(CasProtocol.CAS30); casConfiguration.setPrefixUrl(prefixUrl); return casConfiguration; } @Bean public CasClient casClient() { CasClient casClient = new CasClient(); casClient.setConfiguration(casConfiguration()); casClient.setCallbackUrl(callbackUrl); return casClient; } /** * 路径过滤设置 * @return */ @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition(); definition.addPathDefinition("/callback", "callbackFilter"); definition.addPathDefinition("/logout", "logoutFilter"); definition.addPathDefinition("/**", "casSecurityFilter"); return definition; } /** * 由于cas代理了用户,所以必须通过cas进行创建对象 * * @return */ @Bean protected SubjectFactory subjectFactory() { return new Pac4jSubjectFactory(); } /** * 对过滤器进行调整 * * @param securityManager * @return */ @Bean protected ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { //把subject对象设为subjectFactory ((DefaultSecurityManager) securityManager).setSubjectFactory(subjectFactory()); ShiroFilterFactoryBean filterFactoryBean = super.shiroFilterFactoryBean(); filterFactoryBean.setSecurityManager(securityManager); filterFactoryBean.setFilters(filters()); return filterFactoryBean; } /** * 对shiro的过滤策略进行明确 * @return */ @Bean protected Map<String, Filter> filters() { //过滤器设置 Map<String, Filter> filters = new HashMap<>(); filters.put("casSecurityFilter", casSecurityFilter()); CallbackFilter callbackFilter = new CallbackFilter(); callbackFilter.setConfig(casConfig()); filters.put("callbackFilter", callbackFilter); LogoutFilter logoutFilter = new LogoutFilter(); logoutFilter.setConfig(casConfig()); filters.put("logoutFilter", logoutFilter); return filters; } }

token生成


@RequestMapping("/user/login")
public Object login(HttpServletRequest request, HttpServletResponse response) {
    Map<String, Object> model = new HashMap<>();
    J2EContext context = new J2EContext(request, response);
    final ProfileManager<CasRestProfile> manager = new ProfileManager(context);
    final Optional<CasRestProfile> profile = manager.get(true);
    //获取ticket
    TokenCredentials tokenCredentials = casRestFormClient.requestServiceTicket(serviceUrl, profile.get(), context);
    //根据ticket获取用户信息
    final CasProfile casProfile = casRestFormClient.validateServiceTicket(serviceUrl, tokenCredentials, context);
    //生成jwt token
    String token = generator.generate(casProfile);
    model.put("token", token);
    return new HttpEntity<>(model);
}

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/70240.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!