目录
- 实现思路
- 最终实现的效果为
- 项目结构
- 项目的结构为
- 对于接口的传参数
- 获取token
- 刷新token
- 携带token访问资源
实现思路
使用oauth2+redis+mysql来完成登陆校验,本案例使用oauth2简单的密码模式来实现。
最终实现的效果为
- 登陆页面不设置权限审核,用户通过登陆界面登陆,输入账户密码,后端接收到账户密码之后会去数据库验证,如果验证通过,则返回token给前端。
- 除了登陆页面之外,其余的页面访问的时候会进行权限的鉴定,如果携带的token对应用户的权限不足或没有携带token、携带了错误的token,不允许访问。
- token具有时限,超时token会失效,可以通过refresh_token来刷新token的持续时间。
项目结构
项目的结构为
├─.idea
│ └─dictionaries
├─log
├─src
│ ├─main
│ │ ├─java
│ │ │ └─Rush
│ │ │ ├─config
│ │ │ ├─controller
│ │ │ ├─mapper
│ │ │ ├─pojo
│ │ │ ├─service
│ │ │ └─util
│ │ └─resource
│ └─test
│
└─target
项目的重点其实也就在于config包内
config包内定义了4个类:
- AuthorizationServerConfig
- ResourceServerConfig
- WebSecurityConfig
- CORSFilter「额外针对OAuth跨域问题」
除此之外,继承了UserDetails接口的User类,继承UserDetailsService类的UserService类也很关键。
除了这6个类之外,别的类和普通mybatis项目无异
AuthorizationServerConfig
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth.provider.token.store.redis.RedisTokenStore;
import org.springframework.web.bind.annotation.CrossOrigin;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig
extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
UserDetailsService userDetailsService;
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()
.withClient("password")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds()
.resourceIds("rid")
.scopes("all")
.secret("$a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.allowFormAuthenticationForClients();
}
}
ResourceServerConfig
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.web.bind.annotation.CrossOrigin;
@Configuration
@EnableResourceServer
public class ResourceServerConfig
extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.resourceId("rid").stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasAnyRole("user","admin")
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/home").permitAll()
.and()
.csrf().disable();
}
}
WebSecurityConfig
import bocRush.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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.CrossOrigin;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
UserService userService;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
protected UserDetailsService userDetailsService() {
return super.userDetailsService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.inMemoryAuthentication()
// .withUser("admin")
// .password("$a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
// .roles("admin")
// .and()
// .withUser("sang")
// .password("$a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
// .roles("user");
auth.userDetailsService(userService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/oauth/**").authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.and().cors()
.and().csrf().disable();
}
}
CORSFilter
/**
* Date :/3/25 17:48
* Author : nicolas
*/
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*全局跨域配置*/
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration
public class CORSFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE,PATCH,HEAD");
response.setHeader("Access-Control-Allow-Max-Age", "");
response.setHeader("Access-Control-Allow-Headers", "*");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
@Override
public void destroy() {
}
}
User
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean locked;
private List<Role> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
}
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return !locked;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
//省略getter/setter
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public Boolean getLocked() {
return locked;
}
public void setLocked(Boolean locked) {
this.locked = locked;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
}
UserService
import bocRush.mapper.UserInfoMapper;
import bocRush.mapper.UserMapper;
import bocRush.pojo.User;
import bocRush.pojo.UserInfo;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserService implements UserDetailsService {
@Autowired
UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userMapper.loadUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("账户不存在!");
}
user.setRoles(userMapper.getUserRolesByUid(user.getId()));
//System.out.println(user.getRoles().get().getName() + " --- " + user.getUsername());
return user;
}
}
对于接口的传参数
获取token
- 使用POST方法
http://localhost:8080/oauth/token?username=sang&password=123&grant_type=password&client_id=password&scope=all&client_secret=123
刷新token
- 使用POST方法
http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=1a1c67a0-5f9b-49b1-9f95-dc7889c85cf5&client_id=password&client_secret=123
携带token访问资源
在url内直接携带token
- GET方法
http://localhost:8080/user/hello?access_token=9bdde947-19b7-46fe-8fe0-0f2804150768
在header内携带token
- GET方法
http://localhost:8080/admin/hello
------------Header----------------
Authorization : bearer 3929d92d-f5be-4b2d-9223-8b13e2412f14
Accept : application/json