失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > OAuth2.0+SpringSecurity+Jwt实现系统的登录认证 用户授权

OAuth2.0+SpringSecurity+Jwt实现系统的登录认证 用户授权

时间:2019-01-12 07:45:28

相关推荐

OAuth2.0+SpringSecurity+Jwt实现系统的登录认证 用户授权

系统的登录认证、用户授权实现

本次从以下几个内容进行分享:

使用Jwt生成公钥和私钥根据Jwt令牌实现登录认证完成用户的鉴权和授权自定义项目跳转的登录页面

这是本次实现的流程图:

使用JWT生成公钥和私钥

JWT令牌生成采用非对称加密算法

1、生成密钥证书

下边命令生成密钥证书,采用RSA算法每个证书包含公钥和私钥

keytool -genkeypair -alias changgou -keyalg RSA -keypass changgou -keystore changgou.jks -storepass changgou

Keytool 是一个java提供的证书管理工具

-alias : 密钥的别名

-keyalg :使用的hash算法

-keypass :密钥的访问密码

-keystore :密钥库文件名,xc.keystore保存了生成的证书

-storepass :密钥库的访问密码

查询证书信息:

keytool -list -keystore changgou.jks

2、导出公钥

openssl是一个加解密工具包,这里使用openssl来导出公钥信息。

安装 openssl:/products/Win32OpenSSL.html 安装资料目录下的Win64OpenSSL-1_1_1b.exe 配置openssl的path环境变量,

cmd进入changgou.jks文件所在目录执行如下命令:

keytool -list -rfc --keystore changgou.jks | openssl x509 -inform pem -pubkey

公钥:

-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvFsEiaLvij9C1Mz+oyAm t47whAaRkRu/8kePM+X8760UGU0RMwGti6Z9y3LQ0RvK6I0brXmbGB/RsN38PVnh cP8ZfxGUH26kX0RK+tlrxcrG+HkPYOH4XPAL8Q1lu1n9x3tLcIPxq8ZZtuIyKYEm oLKyMsvTviG5flTpDprT25unWgE4md1kthRWXOnfWHATVY7Y/r4obiOL1mS5bEa/ iNKotQNnvIAKtjBM4RlIDWMa6dmz+lHtLtqDD2LF1qwoiSIHI75LQZ/CNYaHCfZS xtOydpNKq8eb1/PGiLNolD4La2zf0/1dlcr5mkesV570NxRmU1tFm8Zd3MZlZmyv 9QIDAQAB -----END PUBLIC KEY-----

将上边的公钥拷贝到文本public.key文件中,合并为一行,可以将它放到需要实现授权认证的工程中。

根据Jwt令牌实现登录认证

准备工作:

数据库准备相应的表

准备配置类

配置文件

server:port: 9200spring:application:name: user-oauthcloud:nacos:discovery:server-addr: ${ys.nacos.server}datasource:url: ${ys.ds.userurl}driver-class-name: ${ys.ds.driver}# url: jdbc:mysql://192.168.23.170:3305/yinjoer_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTCusername: ${ys.ds.username}password: ${ys.ds.password}redis:host: ${ys.redis}encrypt:key-store:location: classpath:/changgou.jkssecret: changgoualias: changgoupassword: changgouauth:ttl: 3600 #token存储到redis的过期时间clientId: changgouclientSecret: changgoucookieDomain: localhostcookieMaxAge: -1

具体实现

1.Service接口

@Componentpublic interface AuthService {/*** * @param username 用户名* @param password 用户密码* @param clientId 客户端Id* @param clientsecret 客户端密码* @return*/AuthToken login(String username, String password, String clientId, String clientsecret);}

2.Service实现类

/*** 认证实现类* @author ys*/@Service@Transactionalpublic class AuthServiceImpl implements AuthService {@Autowiredprivate RestTemplate restTemplate;@Autowiredprivate LoadBalancerClient loadBalancerClient;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Value("${auth.ttl}")private Long ttl;@Overridepublic AuthToken login(String username, String password, String clientId, String clientsecret) {ServiceInstance instance = loadBalancerClient.choose("user-oauth");URI uri = instance.getUri();String url = uri+"/oauth/token";MultiValueMap<String, String> body = new LinkedMultiValueMap<>();body.add("grant_type","password");body.add("username", username);body.add("password", password);MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();headers.add("Authorization", this.getHttpBasic(clientId, clientsecret));HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){@Overridepublic void handleError(ClientHttpResponse response) throws IOException {if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) {super.handleError(response);}}});ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, Map.class);Map responseEntityBody = responseEntity.getBody();if (responseEntityBody == null || responseEntityBody.get("access_token") == null ||responseEntityBody.get("jti") == null || responseEntityBody.get("refresh_token") == null) {throw new RuntimeException("申请令牌失败");}AuthToken authToken = new AuthToken();authToken.setAccessToken((String) responseEntityBody.get("access_token"));authToken.setRefreshToken((String) responseEntityBody.get("refresh_token"));authToken.setJti((String) responseEntityBody.get("jti"));stringRedisTemplate.boundValueOps(authToken.getJti()).set(authToken.getAccessToken(), ttl, TimeUnit.SECONDS);return authToken;}private String getHttpBasic(String clientId, String clientsecret) {String temp = clientId + ":" + clientsecret;byte[] bytes = Base64Utils.encode(temp.getBytes());return "Basic " + new String(bytes);}}

3.Controller

/*** 认证控制层* @author ys*/@RestController@RequestMapping("/oauth")public class AuthController {@Autowiredprivate AuthService authService;@Value("${auth.clientId}")private String clientId;@Value("${auth.clientSecret}")private String clientSecret;@Value("${auth.cookieDomain}")private String cookieDomain;@Value("${auth.cookieMaxAge}")private int cookieMaxAge;@PostMapping("/login")public Result login(@RequestParam("username") String username, @RequestParam("password") String password,HttpServletResponse response, HttpServletRequest request) {if (StringUtils.isEmpty(username)) {throw new RuntimeException("用户不存在");}if (StringUtils.isEmpty(password)) {throw new RuntimeException("密码错误");}AuthToken token = authService.login(username, password, clientId, clientSecret);HttpServletResponse servletResponse = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();CookieUtil.addCookie(response, cookieDomain, "/", "uid", token.getJti(), cookieMaxAge, false);return new Result(true, StatusCode.OK, "获取Token成功", token.getJti());}}

4.将登录请求放行,在WebSecurityConfig类中configure(),添加放行路径

/**** 忽略安全拦截的URL* @param web* @throws Exception*/@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/oauth/login","/oauth/logout","/oauth/tologin","/login.html","/css/**","/data/**","/fonts/**","/img/**","/js/**");}

完成用户的授权

OAuth授权模式有四种:

1.授权码模式(Authorization Code)

2.隐式授权模式(Implicit)

3.密码模式(Resource Owner Password Credentials)

4.客户端模式(Client Credentials)

新建一个需要授权的服务,添加OAuth的依赖

将public.key添加到resource目录下

编写配置类

@Configuration@EnableResourceServer@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)//激活方法上的PreAuthorize注解public class ResourceServerConfig extends ResourceServerConfigurerAdapter {//公钥private static final String PUBLIC_KEY = "public.key";/**** 定义JwtTokenStore* @param jwtAccessTokenConverter* @return*/@Beanpublic TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {return new JwtTokenStore(jwtAccessTokenConverter);}/**** 定义JJwtAccessTokenConverter* @return*/@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();converter.setVerifierKey(getPubKey());return converter;}/*** 获取非对称加密公钥 Key* @return 公钥 Key*/private String getPubKey() {Resource resource = new ClassPathResource(PUBLIC_KEY);try {InputStreamReader inputStreamReader = new InputStreamReader(resource.getInputStream());BufferedReader br = new BufferedReader(inputStreamReader);return br.lines().collect(Collectors.joining("\n"));} catch (IOException ioe) {return null;}}/**** Http安全配置,对每个到达系统的http请求链接进行校验* @param http* @throws Exception*/@Overridepublic void configure(HttpSecurity http) throws Exception {//所有请求必须认证通过http.authorizeRequests()//下边的路径放行.antMatchers("/user/add","/user/load/**"). //配置地址放行permitAll().anyRequest().authenticated(); //其他地址需要认证授权}}

访问user服务就需要携带Jwt令牌才可以,不然会出现下面的错误

}"error": "unauthorized", "error_description": "Full authentication is required to access this resource" }

在http header中添加 Authorization: Bearer 令牌就可以访问了。

自定义项目跳转的登录页面

修改配置类WebSecurityConfig添加登录页面的路径

@Overridepublic void configure(HttpSecurity http) throws Exception {http.csrf().disable().httpBasic() //启用Http基本身份验证.and().formLogin() //启用表单身份验证.and().authorizeRequests() //限制基于Request请求访问.anyRequest().authenticated(); //其他请求都需要经过验证http.formLogin().loginPage("/oauth/tologin").loginProcessingUrl("/oauth/login");}

放行登录页面和相关的静态资源

/**** 忽略安全拦截的URL* @param web* @throws Exception*/@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/oauth/login","/oauth/logout","/oauth/tologin","/login.html","/css/**","/data/**","/fonts/**","/img/**","/js/**");}

编写登录页面的跳转路径

login:function () {app.msg="正在登录";axios.post("/oauth/login?username="+app.username+"&password="+app.password).then(function (response) {if (response.data.flag){app.msg="登录成功";} else{app.msg="登录失败";}})}

有些代码和配置因为太繁琐我就没写到里面,以上就是本次分享,如果有不恰当的地方,欢迎指正。

如果觉得《OAuth2.0+SpringSecurity+Jwt实现系统的登录认证 用户授权》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。