本文将采用springboot去整合springsecurity,采用oauth2.0授权认证,使用jwt对token增强。本文仅为学习记录,如有不足多谢提出。
一般资源服务器和授权是不放在一起的我为了方便先放在一起,直接在授权处开启资源服务。所有我用授权码模式时会请求不到授权码,其他模式均可获取token并用token访问本项目资源。 后续更新资源服务的配置。
<!-- springSecurity-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Oauth2-->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
<!-- jwt增强-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.0.RELEASE</version>
</dependency>
package com.fxj.springsecurityoauth2.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import java.util.Collection;
import java.util.List;
@Data
@TableName("sys_user")
public class SysUser implements UserDetails {
@TableId(type = IdType.AUTO)
private Long id;
@TableField("username")
private String username;
@TableField("account")
private String account;
@TableField("password")
private String password;
@TableField(exist = false)
private List<SysRole> roleList;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roleList;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public String getPassword() {
return new BCryptPasswordEncoder().encode(password);
}
}
package com.fxj.springsecurityoauth2.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
@TableName("sys_role")
@Data
public class SysRole implements GrantedAuthority {
@TableId(value = "id",type = IdType.AUTO)
private Long id;
@TableField("role_name")
private String roleName;
@TableField("role_dec")
private String roleDec;
@Override
public String getAuthority() {
return roleName;
}
}
此处有两种方式第一是在接口中继承UserDetailsService接口,第二种是在service实现类中继承UserDetailsService
public interface UserService extends UserDetailsService {
// SysUser Login(Principal principal,String userName, String Password) throws HttpRequestMethodNotSupportedException;
}
//@Service("myService")
@Service
public class UserDetailServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Resource
private RoleMapper roleMapper;
@Resource
private UserRoleMapper userRoleMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
SysUser user = new SysUser();
List<SysUserRole> userRoles = new ArrayList<>();
List<SysRole> roles = new ArrayList<>();
List<Integer> rIds = new ArrayList<>();
user = userMapper.selectOne(new QueryWrapper<SysUser>().lambda().eq(SysUser::getUsername,s));
userRoles = userRoleMapper.selectList(new QueryWrapper<SysUserRole>().lambda().eq(SysUserRole::getUId,user.getId()));
userRoles.forEach(a->{
rIds.add(a.getRId());
});
roles = roleMapper.selectList(new QueryWrapper<SysRole>().lambda().in(SysRole::getId,rIds));
user.setRoleList(roles);
return user;
}
// @Override
// public SysUser Login(Principal principal,String userName, String password) throws HttpRequestMethodNotSupportedException {
// SysUser sysUser = new SysUser();
// sysUser = userMapper.selectOne(new QueryWrapper().lambda()
// .eq(SysUser::getUsername,userName).eq(SysUser::getPassword,password));
// return sysUser;
// }
}
@Configuration
public class AutoConfig {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//
// public AuthenticationManager authenticationManager(){
// return new AuthenticationManagerBuilder().getObject();
// }
}
/**
* JWT内容增强器
* Created by macro on 2020/6/19.
*/
@Component
public class JwtTokenEnhancer extends JwtAccessTokenConverter {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
SysUser securityUser = (SysUser) authentication.getPrincipal();
Map<String, Object> info = new HashMap<>();
//把用户ID设置到JWT中
info.put("id", securityUser.getId());
// info.put("client_id",securityUser.getClientId());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
return accessToken;
}
}
package com.fxj.springsecurityoauth2.config;
import com.fxj.springsecurityoauth2.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import javax.annotation.Resource;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private UserService userDetailService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService).passwordEncoder(passwordEncoder);
}
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/allow").permitAll()
.antMatchers("/test/**").authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login")
.permitAll()
.and()
.httpBasic()
.and()
.csrf()
.disable();
}
}
package com.fxj.springsecurityoauth2.config;
import com.fxj.springsecurityoauth2.componet.JwtTokenEnhancer;
import com.fxj.springsecurityoauth2.service.UserService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.List;
/**
* 认证服务配置
*/
@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class Oauth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserService userDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenEnhancer jwtTokenEnhancer;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("admin-app")
.secret(passwordEncoder.encode("123456"))
.scopes("all")
.authorizedGrantTypes("password", "refresh_token","authorization_code")
.accessTokenValiditySeconds(3600*24)
.refreshTokenValiditySeconds(3600*24*7)
.redirectUris("www.baidu.com")
.and()
.withClient("portal-app")
.secret(passwordEncoder.encode("123456"))
.scopes("all")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(3600*24)
.refreshTokenValiditySeconds(3600*24*7)
.redirectUris("www.baidu.com");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
List<TokenEnhancer> delegates = new ArrayList<>();
delegates.add(jwtTokenEnhancer);
delegates.add(accessTokenConverter());
enhancerChain.setTokenEnhancers(delegates); //配置JWT的内容增强器
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService) //配置加载用户信息的服务
.accessTokenConverter(accessTokenConverter())
.tokenEnhancer(enhancerChain)
.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// .tokenKeyAccess("permitAll()")
// .checkTokenAccess("permitAll()")
security.allowFormAuthenticationForClients();
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
//简单对称加密,可使用
jwtAccessTokenConverter.setSigningKey("123456");
return jwtAccessTokenConverter;
}
/**
* 密钥对jwt.jks为resource下的秘钥对文件(具体生成百度)
* 此处仅为简单对称加密,无相关文件
*/
// @Bean
// public KeyPair keyPair() {
// //从classpath下的证书中获取秘钥对
// KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray());
// return keyStoreKeyFactory.getKeyPair("jwt", "123456".toCharArray());
// }
}
package com.fxj.springsecurityoauth2.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/allow").permitAll()
.antMatchers("/test/**").authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login")
.permitAll()
.and()
.httpBasic()
.and()
.csrf()
.disable();
}
}
使用token请求受保护的资源
不受保护的资源
使用授权码模式测试时不能在一个项目中开启@EnableResourceServer,否则请求不到授权码。
项目地址:https://github.com/fuxiaojunStudy/springsecurity-oauth2.0.git
到此结束。