本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云
一个网站的登录页面就相当于一个人的脸,脸往往给人留下第一印象。印象好,就会带来好体验。好体验就可能有更高的活跃度,所以像我前面《SpringBoot + spring-security-oauth2 实现仿微信,QQ,微博等授权认证》中的漂亮的登录页面该如何定制呢?本文,我们一起来实践一把吧。
严格来说,这个登录页面的定制化,并不算 spring-security-oauth2 中的功能,而是靠 Spring Security 来实现的。在 OAuth2 中我们可以针对不同的应用有不同的登录页面,这个登录页面的代码在 Server 端,认证我们可以放在客户端 Client 中。
实现的原理很简单,就是当我们要访问未授权的页面时,进行拦截,通过 @EnableOAuth2Sso 注解,让它通过 OAuth2 到 AuthenticationProvider 中去认证。
首先,我们要实现 WebSecurityConfigurerAdapter,配置哪些资源需要拦截,哪些不需要认证。
@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
@EnableWebSecurity
static class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.antMatcher("/oauth/**")
.authorizeRequests()
.antMatchers("/oauth/index").permitAll()
.antMatchers("/oauth/token").permitAll()
.antMatchers("/oauth/check_token").permitAll()
.antMatchers("/oauth/confirm_access").permitAll()
.antMatchers("/oauth/error").permitAll()
.antMatchers("/oauth/approvale/confirm").permitAll()
.antMatchers("/oauth/approvale/error").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/oauth/index")
.loginProcessingUrl("/oauth/login");
}
@Autowired
private CustomAuthenticationProvider authenticationProvider;
}
登录认证部分,我们通过实现 AuthenticationProvider 接口,对接数据库来实现。
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private AccountService accountService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
Account account = accountService.authUser(name, password);
if (account == null) {
throw new AuthenticationCredentialsNotFoundException("Account is not found.");
}
List<GrantedAuthority> grantedAuths = AuthorityUtils.createAuthorityList(account.getRoleString());
return new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
业务逻辑层 AccountService 的代码如下:
@Service
public class AccountService {
@Autowired
private AccountRepository accountRepository;
public Account authUser(String userName, String password) {
Account u = accountRepository.findByUserName(userName);
if (u == null) {
return null;
}
if (!u.getPassword().equals(password)) {
return null;
}
return u;
}
}
AccountRepository 代码如下:
public interface AccountRepository {
@Select("select id, user_name as userName, email, password, role_string as roleString from account where user_name=#{user_name}")
Account findByUserName(String userName);
}
实体类:
@SuppressWarnings("serial")
public class Account implements Serializable {
private Integer id;
private String userName;
private String email;
private String password;
private String roleString;
// ...setter/getter
}
SQL:
CREATE TABLE account
(
id serial NOT NULL,
user_name character varying(50),
email character varying(255),
password character varying(512),
role_string character varying(50),
CONSTRAINT account_pkey PRIMARY KEY (id)
);
INSERT INTO account(user_name, email, password, role_string)
VALUES ('user', 'user@sample.com', '123', 'ROLE_USER');
扫描包:
@Configuration
@MapperScan("com.rensanning")
@EnableTransactionManagement(proxyTargetClass = true)
static class RepositoryConfig {
}
登录页面代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<script src="https://cdn.bootcss.com/vue/2.5.17/vue.min.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
</head>
<body>
<div class="login-box" id="app" >
<el-form action="/auth/login" method="post" label-position="left" label-width="0px" class="demo-ruleForm login-container">
<h2 class="title" >统一认证登录平台</h2>
<el-form-item>
<el-input type="text" name="username" v-model="username" auto-complete="off" placeholder="账号"></el-input>
</el-form-item>
<el-form-item>
<el-input type="password" name="password" v-model="password" auto-complete="off" placeholder="密码"></el-input>
</el-form-item>
<el-form-item style="width:100%; text-align:center;">
<el-button type="primary" style="width:47%;" @click.native.prevent="reset">重 置</el-button>
<el-button type="primary" style="width:47%;" native-type="submit" :loading="loading">登 录</el-button>
</el-form-item>
<el-form>
</div>
</body>
<script type="text/javascript">
new Vue({
el : '#app',
data : {
loading: false,
username: 'admin',
password: '123'
},
methods : {
reset: function() {
this.username = 'admin'
this.password = '123'
}
}
})
</script>
<style lang="scss" scoped>
.login-container {
-webkit-border-radius: 5px;
border-radius: 5px;
-moz-border-radius: 5px;
background-clip: padding-box;
margin: 100px auto;
width: 320px;
padding: 35px 35px 15px 35px;
background: #fff;
border: 1px solid #eaeaea;
box-shadow: 0 0 25px #cac6c6;
}
.title {
margin: 0px auto 20px auto;
text-align: center;
color: #505458;
}
</style>
</html>
注意,pom.xml 中,需要引入 spring-boot-starter-thymeleaf,最终运行效果:
参考资料
- https://stackoverflow.com/questions/29618658/spring-how-to-create-a-custom-access-and-refresh-oauth2-token
- https://stackoverflow.com/questions/29345508/spring-oauth2-custom-oauth-approval-page-at-oauth-authorize

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!
本文原文出处:业余草: » spring-security-oauth2 自定义登录页面